jQuery 的 ready 函数是如何工作的?

学习 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 完成了页面检测代码的注册。这样。当页面完全加载之后,我们注册的函数就被调用了。

时间: 2024-08-12 10:23:17

jQuery 的 ready 函数是如何工作的?的相关文章

jQuery 的 ready 函数是如何工作的?(源码分析)

如果你使用过 jQuery , 就必然使用过 ready 函数,它用来注册当页面准备好之后可以执行的函数. 问题来啦,我们的页面什么时候准备好了呢? 1. onload 事件 最基本的处理方式就是页面的 onload 事件,我们在处理这个事件的时候,可以有多种方式,即可以通过 HTML 方式,直接写在 body 元素的开始标记中,也可以使用事件注册的方式来使用,这又可以分为 DOM0 方式和 DOM2 方式.再考虑到浏览器的兼容性,使用 DOM2 方式写出来,如下所示. if (document

JQuery的ready函数与JS的onload的区别

JQuery的ready函数与JS的onload的区别:1.执行时间window.onload必须等到页面内包括图片的所有元素加载完毕后才能执行.$(document).ready()是DOM结构绘制完毕后就执行,不必等到加载完毕. 2.编写个数不同window.onload不能同时编写多个,如果有多个window.onload方法,只会执行一个$(document).ready()可以同时编写多个,并且都可以得到执行 3.简化写法window.onload没有简化写法$(document).r

JQuery的ready函数与JS的onload的区别详解

JQuery的ready函数与JS的onload的区别:1.执行时间window.onload必须等到页面内包括图片的所有元素加载完毕后才能执行.$(document).ready()是DOM结构绘制完毕后就执行,不必等到加载完毕. 2.编写个数不同window.onload不能同时编写多个,如果有多个window.onload方法,只会执行一个$(document).ready()可以同时编写多个,并且都可以得到执行 3.简化写法window.onload没有简化写法$(document).r

jquery中ready函数,$(function(){})与自执行函数的区别

<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title> <script type="text/javascript" src="js/jquery-1.12.1.js"></script> <script type=&q

[Javascript]jquery $(document).ready() 与window.onload的区别

引用:http://www.jb51.net/article/21628.htm Jquery中$(document).ready()的作用类似于传统JavaScript中的window.onload方法,不过与window.onload方法还是有区别的. 1.执行时间         window.onload必须等到页面内包括图片的所有元素加载完毕后才能执行.         $(document).ready()是DOM结构绘制完毕后就执行,不必等到加载完毕. 2.编写个数不同      

不能调用jquery中ready里面定义的函数?

现象:不能调用jquery中ready里面定义的函数 源码:<script type="text/javascript"> $(document).ready(function(e) { function test(){ alert('test!'); } }); test(); // 报错,test()未定义 </script> 解析:ready也相当于一个函数,即新建一局部函数作用域,外面 当然不可用.和js的onload函数差不多,就比如 function

chrome浏览器下用jQuery的load函数来跨域加载页面,响应状态status为(canceled)是什么情况? JSON和JSONP,也许你会豁然开朗,含jQuery用例

http://www.cnblogs.com/dowinning/archive/2012/04/19/json-jsonp-jquery.html 问题来源:http://q.cnblogs.com/q/63123/ 问题出现的环境: 1:两个domain相同端口号不同的域名. 2:MVC4.0框架 3:从一个域名下用  jQuery.load("另一个域名下的页面"): 4:响应失败 : 响应状态status为(canceled) 请问这是什么情况呢 ?  我在IE下就没问题,可以

[转]jQuery DOM Ready

一直以来,各种JS最佳实践都会告诉我们,将JS放在HTML的最后,即</body>之前,理由就是:JS会阻塞下载,而且,在JS中很有可能有对DOM的操作,放在HTML的最后,可以尽可能的保证JS的执行在DOM加载完成之后.而如果放在onload事件中执行,如果页面有很多图像,那么页面的onload事件要过很久才会触发,因此DOM Ready事件就是最好的执行JS的时间了. 所以,如果有个DOM Ready事件就好了,虽然现代浏览器已经支持DOMContentLoaded事件,但是我们还是得处理

JQuery中trim函数的具体实现代码

由于Javascript 1.8.1 之前的版本,没有内置 trim 函数,所以 JQuery 对它有自己的实现.不同的JQuery版本,trim函数的实现也不尽相同. 阅读本文需要掌握正则表达式用法,如果不是很了解,建议阅读这个.鉴于正则表达式的强大用途(在各种语言如JS,Python,Ruby,Java中都会用到),建议重点学习并掌握. JQuery 1.7.2版本 // 截取的部分源码,不是完整语句,旨在说明实现过程 trimLeft = /^\s+/, trimRight = /\s+$