測試代碼:
$('#lk').ready(
function(){
alert($(this).width());
}
);
首先$('#lk')生成jQuery對象并選擇對應的DOM,然后ready進入的是
(這里,jQuery.fn.extend擴展的是jQuery對象的方法和屬性,而jQuery.extend擴展的是jQuery類的方法和屬性。)
jQuery.fn.ready = function( fn ) {
// Add the callback
jQuery.ready.promise().done( fn );
return this;
};
往下跟蹤jQuery.ready.promise()
jQuery.ready.promise = function( obj ) {
if ( !readyList ) {
readyList = jQuery.Deferred();
// Catch cases where $(document).ready() is called after the browser event has already occurred.
// we once tried to use readyState "interactive" here, but it caused issues like the one
// discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
if ( document.readyState === "complete" ) {
// Handle it asynchronously to allow scripts the opportunity to delay ready
setTimeout( jQuery.ready );
// Standards-based browsers support DOMContentLoaded
} else if ( document.addEventListener ) {
// Use the handy event callback
document.addEventListener( "DOMContentLoaded", completed, false );
// A fallback to window.onload, that will always work
window.addEventListener( "load", completed, false );
// If IE event model is used
} else {
// 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();
}
})();
}
}
}
return readyList.promise( obj );
};
readyList一開始是這樣定義的
// The deferred used on DOM ready
var readyList;
但這里執行的時候,readyList已經是一個deferred對象了,在哪定義的?
在jQuery.ready.promise的 if ( !readyList )里面添加一個alert('ok'),發現在頁面載入的時候是執行到了這里的。故readyList是在初始化JQuery對象的時候初始化的。
readyList = jQuery.Deferred();
這樣生成了一個deferred對象,可以參考這篇文章對deferred的理解
jQuery的deferred對象詳解
簡單來說deferred對象是處理回調函數的一個東西,以后再來分析這個對象。
這里的核心就是如何判斷DOM已經加載完成,注意是DOM加載完成而不是所有內容都加載完成(比如圖片等)
在jQuery.ready.promise中的三個判斷就是解決這個問題:
(1)第一次判斷是
if ( document.readyState === "complete" )
這個是IE特有的,如果加載完成了就執行
setTimeout( jQuery.ready );
這個setTimeout其實就是立即執行ready,ready執行的內容是判斷isReady然后去執行回調函數。
(2)第二次判斷是
if ( document.addEventListener )
判斷是不是標準瀏覽器,若是則用DOMContentLoaded事件,從名字都能知道這個事件是干嘛用的。
(3)第三次判斷是
3.1 這里只能查資料了。
如果瀏覽器存在 document.onreadystatechange 事件,當該事件觸發時,
如果 document.readyState=complete 的時候,可視為 DOM 樹已經載入。
不過,這個事件不太可靠,比如當頁面中存在圖片的時候,可能反而在 onload
事件之后才能觸發,換言之,它只能正確地執行于頁面不包含二進制資源或非常少或者被緩存時作為一個備選吧。
3.2 window.attachEvent( "onload", completed );作為一個保險。這樣綁定不會重復嗎?
當然這些都在completed中處理了,有重復的都被解除綁定了。
3.3
try {
top.doScroll("left");
} catch(e) {
return setTimeout( doScrollCheck, 50 );
}
這段代碼很有名
Diego Perini 在 2007 年的時候,報告了一種檢測 IE 是否加載完成的方式,使用 doScroll 方法調用。詳細的說明見這里。
原理是對于 IE 在非 iframe 內時,只有不斷地通過能否執行 doScroll 判斷 DOM 是否加載完畢。
在本例中每間隔 50 毫秒嘗試去執行 doScroll,注意,由于頁面沒有加載完成的時候,調用 doScroll 會導致異常,所以使用了 try -catch 來捕獲異常
到這里就講完了所有檢測DOM加載完成的方法,至于關于回調方面的內容,下回再來研究分析一下。