首頁  >  文章  >  web前端  >  jQuery中ready函數的工作機制

jQuery中ready函數的工作機制

巴扎黑
巴扎黑原創
2017-06-25 10:31:07856瀏覽

jQuery 是一個很棒的腳本庫,由 John Resig 在 2006年1月的BarCamp NYC上釋出第一個版本。你可以在 http://jquery.com/ 下載到最新版本,目前已經是 1.8.3 版了。

學習 jQuery 有許多途徑,我們今天從 jQuery 的 ready 函數開始。本例中的程式碼都來自於 jQuery 腳本庫。

如果你使用過 jQuery , 就必然使用過 ready 函數,它用來註冊當頁面準備好之後可以執行的函數。

問題來啦,我們的頁面什麼時候準備好了呢?

1. onload 事件

最基本的處理方式就是頁面的onload 事件,我們在處理這個事件的時候,可以有多種方式,即可以通過HTML 方式,直接寫在body 元素的開始標記中,也可以使用事件註冊的方式來使用,這又可以分為DOM0 方式和DOM2 方式。再考慮到瀏覽器的兼容性,使用 DOM2 方式寫出來,如下所示。


if (document.addEventListener) {    // A fallback to window.onload, that will always work
    window.addEventListener("load", jQuery.ready, false);    // If IE event model is used} else {    // A fallback to window.onload, that will always work
    window.attachEvent("onload", jQuery.ready);
}

2. DOMContentLoaded 事件

不過 onload 事件要等到所有頁面元素載入完成才會觸發, 包含頁面上的圖片等等。如果網頁上有大量的圖片,效果可想而知,用戶可能在沒有看到圖片的時候,就已經開始操作頁面了,而這時我們的頁面還沒有初始化,事件還沒有註冊上,這豈不是太晚了!

除了大家熟知的onload 事件之外, 與DOM 中的onload 事件相近的,我們還有DOMContentLoaded 事件可以考慮, 基於標準的瀏覽器支持這個事件,  當所有DOM 解析完以後會觸發這個事件。

這樣,對於基於標準的瀏覽器來說,我們也可以註冊這個事件的處理。這樣,我們可能更早地捕獲到載入完成的事件。


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);

}

3. onreadystatechange 事件

不標準的瀏覽器怎麼辦呢?

如果瀏覽器存在 document.onreadystatechange 事件,當該事件觸發時,如果 document.readyState=complete 的時候,可視為 DOM 樹已經載入。

不過,這個事件不太可靠,例如當頁面中存在圖片的時候,可能反而在onload 事件之後才能觸發,換言之,它只能正確地執行於頁面不包含二進制資源或非常少或者被緩存時作為一個備選吧。


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);    // If IE event model is used} else {    // Ensure firing before onload, maybe late but safe also for iframes
    document.attachEvent("onreadystatechange", DOMContentLoaded);    // A fallback to window.onload, that will always work
    window.attachEvent("onload", jQuery.ready);
}

DOMContentLoaded 函數在做什麼呢?最終還是要呼叫 jQuery.ready 函數。


DOMContentLoaded = function() {    if ( document.addEventListener ) {
        document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
        jQuery.ready();
    } else if ( document.readyState === "complete" ) {        // we're here because readyState === "complete" in oldIE
        // which is good enough for us to call the dom ready!
        document.detachEvent( "onreadystatechange", DOMContentLoaded );
        jQuery.ready();
    }
}

 

4. doScroll 偵測法

MSDN 關於JScript 的一個方法有段不起眼的話,當頁面DOM 未載入完成時,呼叫doScroll 方法時,會產生異常。那我們反過來用,如果不異常,那就是頁面DOM載入完畢了!

Diego Perini 在 2007 年的時候,報告了一種偵測 IE 是否載入完成的方式,使用 doScroll 方法呼叫。詳細的說明請見這裡。
原則是對於 IE 在非 iframe 內時,只有不斷地透過能否執行 doScroll 判斷 DOM 是否載入完畢。在本例中每間隔 50 毫秒嘗試去執行 doScroll,注意,由於頁面沒有載入完成的時候,呼叫 doScroll 會導致異常,所以使用了 try -catch 來捕捉異常。


(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);
        }        // and execute any waiting functions        jQuery.ready();
    }
})();

5. document.readyState 狀態

如果我們註冊ready 函數的時間點太晚了,頁面已經載入完成之後,我們才註冊自己的ready 函數,那就用不著上面的層層檢查了,直接看看當前頁面的readyState 就可以了,如果已經是complete ,那就可以直接執行我們準備註冊的ready 函數了。不過 ChrisS 報告了一個很特別的錯誤狀況,我們需要延遲一下執行。

setTimeout 经常被用来做网页上的定时器,允许为它指定一个毫秒数作为间隔执行的时间。当被启动的程序需要在非常短的时间内运行,我们就会给她指定一个很小的时间数,或者需要马上执行的话,我们甚至把这个毫秒数设置为0,但事实上,setTimeout有一个最小执行时间,当指定的时间小于该时间时,浏览器会用最小允许的时间作为setTimeout的时间间隔,也就是说即使我们把setTimeout的毫秒数设置为0,被调用的程序也没有马上启动。

这个最小的时间间隔是多少呢?这和浏览器及操作系统有关。在John Resig的新书《Javascript忍者的秘密》一书中提到

    Browsers all have a 10ms minimum delay on OSX and a(approximately) 15ms delay on Windows.(在苹果机上的最小时间间隔是10毫秒,在Windows系统上的最小时间间隔大约是15毫秒)

,另外,MDC中关于setTimeout的介绍中也提到,Firefox中定义的最小时间间隔(DOM_MIN_TIMEOUT_VALUE)是10毫秒,HTML5定义的最小时间间隔是4毫秒。既然规范都是这样写的,那看来使用setTimeout是没办法再把这个最小时间间隔缩短了。

这样,通过设置为 1, 我们可以让程序在浏览器支持的最小时间间隔之后执行了。


// 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:15if (document.readyState === "complete") {    // 延迟 1 毫秒之后,执行 ready 函数
    setTimeout(jQuery.ready, 1);
}

 6. 完整的代码

在 jQuery 中完整的代码如下所示。位于 jQuery 1.8.3 源代码的 #842 行。


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, 1 );        // Standards-based browsers support DOMContentLoaded
        } else 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 );        // If IE event model is used
        } else {            // Ensure firing before onload, maybe late but safe also for iframes
            document.attachEvent( "onreadystatechange", DOMContentLoaded );            // A fallback to window.onload, that will always work
            window.attachEvent( "onload", jQuery.ready );            // 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 );
                        }                        // and execute any waiting functions                        jQuery.ready();
                    }
                })();
            }
        }
    }    return readyList.promise( obj );
};

那么,又是谁来调用呢?当然是需要的时候,在我们调用 ready 函数的时候,才需要注册这些判断页面是否完全加载的处理,这段代码在 1.8.3 中位于代码的 #244 行,如下所示:


ready: function( fn ) {    // Add the callback    jQuery.ready.promise().done( fn );    return this;
}

在页面上引用 jQuery 脚本库之后,执行了 jQuery 的初始化函数,初始化函数中创建了 ready 函数。我们在通过 ready 函数注册事件处理之前,jQuery 完成了页面检测代码的注册。这样。当页面完全加载之后,我们注册的函数就被调用了。

以上是jQuery中ready函數的工作機制的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn