jQuery中ready與load事件(來自慕課網)
jQuery有3種針對文檔加載的方法
$(document).ready(function() { // ...代碼... }) //document ready 簡寫 $(function() { // ...代碼... }) $(document).load(function() { // ...代碼... })
一個是ready一個是load,這兩個到底有什么區別呢?
ready與load誰先執行:
大家在面試的過程中,經常會被問到一個問題:ready與load那一個先執行,那一個后執行?答案是ready先執行,load后執行。
DOM文檔加載的步驟:
要想理解為什么ready先執行,load后執行就要先了解下DOM文檔加載的步驟:
(1) 解析HTML結構。 (2) 加載外部腳本和樣式表文件。 (3) 解析並執行腳本代碼。 (4) 構造HTML DOM模型。//ready (5) 加載圖片等外部文件。 (6) 頁面加載完畢。//load
從上面的描述中大家應該已經理解了吧,ready在第(4)步完成之后就執行了,但是load要在第(6)步完成之后才執行。
結論:
ready與load的區別就在於資源文件的加載,ready構建了基本的DOM結構,所以對於代碼來說應該越快加載越好。在一個高速瀏覽的時代,沒人願意等待答案。假如一個網站頁面加載超過4秒,不好意思,你1/4的用戶將面臨着流失,所以對於框架來說用戶體驗是至關重要的,我們應該越早處理DOM越好,我們不需要等到圖片資源都加載后才去處理框架的加載,圖片資源過多load事件就會遲遲不會觸發。
我們看看jQuery是如何處理文檔加載時機的問題:
jQuery.ready.promise = function( obj ) { if ( !readyList ) { readyList = jQuery.Deferred(); if ( document.readyState === "complete" ) { // Handle it asynchronously to allow scripts the opportunity to delay ready setTimeout( jQuery.ready ); } else { document.addEventListener( "DOMContentLoaded", completed, false ); window.addEventListener( "load", completed, false ); } } return readyList.promise( obj ); };
jQuery的ready是通過promise給包裝過的,這也是jQuery擅長的手法,統一了回調體系,以后我們會重點談到。
可見jQuery兼容的具體策略:針對高級的瀏覽器,我們當前很樂意用DOMContentLoaded事件了,省時省力。
那么舊的IE如何處理呢?
繼續看jQuery的方案:
// Ensure firing before onload, maybe late but safe also for iframes document.attachEvent( "onreadystatechange", completed ); // A fallback to window.onload, that will always work window.attachEvent( "onload", completed ); // If IE and not a frame // continually check to see if the document is ready var top = false; try { top = window.frameElement == null && document.documentElement; } catch(e) {} if ( top && top.doScroll ) { (function doScrollCheck() { if ( !jQuery.isReady ) { try { // Use the trick by Diego Perini // http://javascript.nwbox.com/IEContentLoaded/ top.doScroll("left"); } catch(e) { return setTimeout( doScrollCheck, 50 ); } // detach all dom ready events detach(); // and execute any waiting functions jQuery.ready(); } })(); }
如果瀏覽器存在 document.onreadystatechange
事件,當該事件觸發時,如果 document.readyState=complete
的時候,可視為 DOM 樹已經載入。不過,這個事件不太可靠,比如當頁面中存在圖片的時候,可能反而在 onload 事件之后才能觸發,換言之,它只能正確地執行於頁面不包含二進制資源或非常少或者被緩存時作為一個備選吧。
針對IE的加載檢測
Diego Perini 在 2007 年的時候,報告了一種檢測 IE 是否加載完成的方式,使用 doScroll 方法調用,詳情可見http://javascript.nwbox.com/IEContentLoaded/。
原理就是對於 IE 在非 iframe 內時,只有不斷地通過能否執行 doScroll 判斷 DOM 是否加載完畢。在上述中間隔 50 毫秒嘗試去執行 doScroll,注意,由於頁面沒有加載完成的時候,調用 doScroll 會導致異常,所以使用了 try -catch 來捕獲異常。
結論:所以總的來說當頁面 DOM 未加載完成時,調用 doScroll 方法時,會產生異常。那么我們反過來用,如果不異常,那么就是頁面DOM加載完畢了。
這都是我們在第一時間內處理ready加載的問題,如果ready在頁面加載完畢后呢?
jQuery就必須針對這樣的情況跳過綁定了:
if ( document.readyState === "complete" ) { // Handle it asynchronously to allow scripts the opportunity to delay ready setTimeout( jQuery.ready ); }
直接通過查看readyState的狀態來確定頁面的加載是否完成了。這里會給一個定時器的最小時間后去執行,主要保證執行的正確。