只要使用過jQuery的,想必對ready都不陌生,$(function(){})和$(document).ready(function(){})的使用更是習以為常。那它內部是怎麼實現的呢,嘿嘿,我們不妨一同學習學習
只要使用過jQuery的,想必對ready都不陌生,$(function(){})和$(document).ready(function(){})的使用更是習以為常。
要說到window.onload與document.ready的區別也能談出個一二,最重要的區別就是:
window.onload是在dom文檔樹以及所有文件都載入完成後,才執行;
而document.ready是,只要dom文檔樹載入完,就執行,且當dom文檔樹載入完就執行的好處就是,當頁面中的圖片等外部資源過多時,window.onload遲遲不能觸發,這時若還沒有綁定事件,用戶點擊按鈕時沒有反應,這不影響用戶體驗麽。
咦,Jquery的ready這麼牛逼,那Jquery是怎麼實現ready這個函數的呢?我們不妨一起來探究探究。
以下Jquery.ready的源碼,截自於jQuery 1.12.0。
1 jQuery.ready.promise = function( obj ) { 2 if ( !readyList ) { 3 4 readyList = jQuery.Deferred(); 5 6 // Catch cases where $(document).ready() is called 7 // after the browser event has already occurred. 8 // we once tried to use readyState "interactive" here, 9 // but it caused issues like the one 10 // discovered by ChrisS here: 11 // http://bugs.jquery.com/ticket/12282#comment:15 12 if ( document.readyState === "complete" ) { 13 14 // Handle it asynchronously to allow scripts the opportunity to delay ready 15 window.setTimeout( jQuery.ready ); 16 17 // Standards-based browsers support DOMContentLoaded 18 } else if ( document.addEventListener ) { 19 20 // Use the handy event callback 21 document.addEventListener( "DOMContentLoaded", completed ); 22 23 // A fallback to window.onload, that will always work 24 window.addEventListener( "load", completed ); 25 26 // If IE event model is used 27 } else { 28 29 // Ensure firing before onload, maybe late but safe also for iframes 30 document.attachEvent( "onreadystatechange", completed ); 31 32 // A fallback to window.onload, that will always work 33 window.attachEvent( "onload", completed ); 34 35 // If IE and not a frame 36 // continually check to see if the document is ready 37 var top = false; 38 39 try { 40 top = window.frameElement == null && document.documentElement; 41 } catch ( e ) {} 42 43 if ( top && top.doScroll ) { 44 ( function doScrollCheck() { 45 if ( !jQuery.isReady ) { 46 47 try { 48 49 // Use the trick by Diego Perini 50 // http://javascript.nwbox.com/IEContentLoaded/ 51 top.doScroll( "left" ); 52 } catch ( e ) { 53 return window.setTimeout( doScrollCheck, 50 ); 54 } 55 56 // detach all dom ready events 57 detach(); 58 59 // and execute any waiting functions 60 jQuery.ready(); 61 } 62 } )(); 63 } 64 } 65 } 66 return readyList.promise( obj ); 67 };
從上面的源碼中,可以看出,jQuery.ready主要通過以下幾個東東來判斷dom文檔樹是否載入完成:
(1) document.readyState
(2) DOMContentLoaded
(3) onreadystatechange
(4) doScroll
下麵,我們就一步一步來解析
1、 document.readyState
readyState是個什麼東東呢?它是document的一個屬性值,返回當前文檔的狀態,該屬性會根據文檔載入情況,返回如下幾個屬性值:
屬性值 |
意義 |
uninitialized |
還未開始載入 |
loading |
載入中 |
interactive |
已載入,文檔與用戶可以開始交互 |
complete |
載入完成 |
從屬性值,可得知,倘若當我們判斷document.readyState為complete,那麼DOM文檔樹就是載入完畢。但,從上面源碼註釋(6--11行)中可以得知ChrisS發現了一個很特別的問題,所以我們在判斷document.readyState === ‘complete’後,執行jQuery.ready,需要延遲一下。但上面的代碼中,怎麼沒有延遲時間呢?
那是因為倘若我們沒有設置延遲時間,setTimeout就會根據當前瀏覽器及操作系統,自動給它設定一個最小延遲時間。在《JavaScript忍者的秘密》中曾提到:
2、 DOMContentLoaded事件
從上面的源代碼註釋中,可以看出DOMContentLoaded是基於標準的瀏覽器的。
那麼它的作用是什麼呢?
當DOM文檔樹載入完成後,即觸發。
所以可以在標準的瀏覽器中判斷DOMContentLoaded,來判斷DOM樹,是否載入完成。
3、 onreadystatechange事件
上面的DOMContentLoaded事件是基於標準的瀏覽器的,那倘若不標準的呢,如IE,則使用onreadystatechange事件。
咦,怎麼感覺如此熟悉。
XMLHttpRequest—>Ajax。想起來了麽。在IE中onreadystatechange是私有化的,即所有元素都存在onreadystatechange事件,而W3C標準中,僅XMLHttpRequest對象中存在onreadystatechange事件。所以當事件觸發時,倘若onreadystatechange === complete,則可視為DOM樹載入完成。
4、 doScroll
從上面的源碼中的註釋(49--50行),可得,Diego Perini報告一種檢測IE下DOM文檔是否載入完成的方法,即,使用doScroll。
咦,上面onreadystatechange事件,不是能處理了嗎?!!
是的,但是它有個弊端,就是當頁面中存在圖片時,可能反而會晚於onload事件後,觸發。
為什麼呢?
因為我們是依據onreadystatechange === complete來判斷的嘛,如果圖片沒載入或者正在載入中,那麼onreadystatechange就不等於complete了哦。
所以為了保險起見,再用doScroll來檢測。
doScroll的原理就是,當頁面DOM未載入完成時,調用doScroll方法,會產生異常。所以利用try-catch來對doScroll捕獲異常就可以判斷DOM文檔是否載入完咯。
好了,jQuery.ready的源碼就解析到這兒,純屬自己觀點,有什麼不對,請狠狠拍磚且交流,謝謝。