jQuery的ready函数源码解读

假如你对$(document).ready()的明白也仅限于在DOM Tree绘制终了后触发,那末,你也应当好好研讨下ready的事情道理,因为,TST的面试官问过我这个题目。。。

一、关于jQuery

jQuery是一个巨大的剧本库,由John Resig在 2006年1月的BarCamp NYC上释出第一个版本。你能够在 http://jquery.com/ 下载到最新版本。这里以jQuery1.8.3为例剖析。
进修jQuery有很多门路,我们本日从jQueryready函数最先。本例中的代码都来自于jQuery剧本库。
假如你运用过jQuery,就必定运用过ready函数,它用来注册当页面预备好以后能够实行的函数。
题目来啦,我们的页面什么时刻预备好了呢?

二、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);
}

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

}

四、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();
    }
}

五、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();
    }
})();

六、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:15
if (document.readyState === "complete") {
    // 耽误 1 毫秒以后,实行 ready 函数
    setTimeout(jQuery.ready, 1);
}

七、完整代码

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完成了页面检测代码的注册。如许。当页面完整加载以后,我们注册的函数就被挪用了。

八、参考

1.jQuery 的 ready 函数是怎样事情的?
2.jQuery ready函数完成道理
3.deferred.done()

    原文作者:jack2wang
    原文地址: https://segmentfault.com/a/1190000005762854
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞