前言:
ready()事件的應用,是大家再熟悉不過的了,學jQuery的第一步,最最常見的程式碼:
jQuery(document).ready(function () { }); jQuery(function () { }); $(document).ready(function () { }); $(function () { });
以上四行程式碼的目的和效果都一樣——待DOM載入完成之後,執行傳入的function函式。
再對jquery稍微熟悉一點的朋友可能知道,這裡的“待DOM載入完成”,不是window.onload事件,window.onload是指“DOM載入完成 + DOM相關的檔案下載完成”。這裡的“DOM載入完成”,不包括“DOM相關的檔案載入完成”。相關的事件是:
- DOMContentLoaded事件(IE9+以及其他瀏覽器)
- onreadystatechange事件(IE9以下瀏覽器)
問題就在這裡。如果知道了這兩個事件,那麼把傳入的function函式關聯到這兩個事件上就行了,而jquery中與ready相關的程式碼洋洋灑灑的寫了那麼多,至於上百行程式碼。這是為何?
原因在於以下幾點:
2. 儲存結構——基於非同步佇列設計:
先看以下程式碼:
//應用ready事件 $(function () { alert(10); }); $(function () { alert(20); }); $(function () { alert(30); });
以上程式碼連續應用了三次ready方法,其實jquery是用本身的jquery.callbacks來處理的。主要程式碼如下:
readyList = jQuery.Callbacks( "once memory" );
readyList.add( fn );
readyList.fireWith( document, [ jQuery ] );
"once":代表add進來的函式只被呼叫一次;
"memory":代表一旦readylist被執行過一次,那麼它後續新增進來的函式會立即按照執行時候的環境和引數執行。
3. 巧妙的事件繫結:
以IE9+和其他瀏覽器支援的DOMContentLoaded為例。先看程式碼:
// Mozilla, Opera and webkit nightlies currently support this event if ( document.addEventListener ) { // Use the handy event callback document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); // A fallback to window.onload, that will always work window.addEventListener( "load", jQuery.ready, false ); }
DOMContentLoaded = function() { document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); jQuery.ready(); };
根據以上程式碼可見,最終DOMContented事件執行的,其實是jQUery.ready()這個工具函式。(注意,jquery.ready()和jquery(document).raedy()不一樣!!,前者是工具函式,後者是例項函式。)
這裡是通過定義一個DOMContentLoaded函式作為橋樑來執行jquery.ready()函式的,這樣做的目的就是為了即使的remove掉document的DOMContentLoaded事件的引用。
document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
單獨把這兩行摘出來,可以看明白,add完了之後,接著remove掉了,在這中間,巧妙的執行了jquery.ready(),這種用法值得學習!
反過來,如果像以下程式碼那麼樣實現,document的DOMContentLoaded事件的引用將無法及時刪除。
//反例 document.removeEventListener( "DOMContentLoaded", jQuery.ready, false );
另外,除了通過瀏覽器的DOMContentLoaded/onreadystatechange方法可以呼叫jquery.ready()函式之外,還有一種巧妙的方式呼叫jquery.ready()函式。
在IE9以下的瀏覽器中,如果當前頁面是頂層(即沒有包含在iframe和friame元素中),呼叫html.doScroll(),直到不丟擲異常,即可呼叫jquery.ready()函式。
1 if ( document.documentElement.doScroll && toplevel ) { 2 doScrollCheck(); 3 } 4 5 /*省略*/ 6 7 try { 8 // If IE is used, use the trick by Diego Perini 9 // http://javascript.nwbox.com/IEContentLoaded/ 10 document.documentElement.doScroll("left"); 11 } catch(e) { 12 setTimeout( doScrollCheck, 1 ); 13 return; 14 } 15 16 // and execute any waiting functions 17 jQuery.ready();
4. 事件執行:
上文中講到,可以通過瀏覽器的DOMContentLoaded/onreadystatechange事件,以及對html.doScroll()的檢測來呼叫jquery.ready()工具函式,jquery.ready()最終將會呼叫非同步佇列的firewith()方法觸發傳入的所有事件。如果是通過js手動呼叫,也可以通過jquery事件系統來呼叫。
readyList.fireWith( document, [ jQuery ] ); if ( jQuery.fn.trigger ) { jQuery( document ).trigger( "ready" ).off( "ready" ); }
其實這其中還有個jquery.holdready()方法用來延遲呼叫,不過比較簡單,也不常用,此處不說了。
5. 總結:
可見,jQuery中的ready()事件並不是我們看起來那麼簡單,瞭解其原理的同時,也應該想想我們在自己設計系統的時候,考慮的時候全面。
- 它用到了非同步佇列,使得使用者可以多次呼叫,順序執行;
- 它的事件繫結考慮到了各種情況,而又充分考慮了資源的釋放;
- 它同時考慮了瀏覽器呼叫的情況,和js手動呼叫的情況。
個人感受:想了解js到底該怎麼用,瞭解js的原始碼和設計,是一個捷徑。