DOMContentLoaded事件是否完全等同于jQuery的.ready()函数?

40
我已经将window.addEventListener('DOMContentLoaded', function() {}); 替换为jQuery的 $(document).bind('ready', function() {});,因为第一个在IE < 9上无法正常工作,我不想为该垃圾浏览器使用.attachEvent() 进行操作,如果可以由jQuery自己很好地处理这个问题。

替换后不久,我注意到DOMContentLoaded事件总是在页面加载/刷新后的0-2毫秒左右被触发(至少这是由我的记录脚本记录的),而.ready()总是需要至少15-20毫秒才会被触发(同样是由记录脚本记录的)。

我只是出于好奇心问一下,为什么会有如此“显着”的延迟?当然,对我来说,jQuery稍后触发该事件没有任何问题。只是因为我想知道所有的答案(并统治世界!:]),所以我不能睡觉!:]

编辑:在.ready()函数文档中,一些用户(Nick (of Nexxar))指出:“jQuery在IE上模拟不存在的“DOMContentLoaded”事件,但使用的机制比其他浏览器上使用的事件要晚得多”。也许这就是我要问的内容?

4个回答

26

假设浏览器支持事件:

  1. 真正的事件可以支持任何document。无论你传递什么,jQuery只会使用它加载时的document
  2. 即使事件已经发生,jQuery也会异步地触发该事件。如果事件已经发生,则附加'DOMContentLoaded'事件将不起作用。

这些浏览器中没有延迟,参见http://jsfiddle.net/rqTAX/3/(记录的偏移量以毫秒为单位)。

对于不支持该事件的浏览器,jQuery也可以使用。它将使用一个不同于真正的DOMContentLoaded的hack机制,并且不一定会像真正的DOMContentLoaded那样立即触发:

// The DOM ready check for Internet Explorer
function doScrollCheck() {
    if ( jQuery.isReady ) {
        return;
    }

    try {
        // If IE is used, use the trick by Diego Perini
        // http://javascript.nwbox.com/IEContentLoaded/
        document.documentElement.doScroll("left");
    } catch(e) {
        setTimeout( doScrollCheck, 1 );
        return;
    }

    // and execute any waiting functions
    jQuery.ready();
}

如果我没有理解错的话,你的答案只是解释了为什么在不支持 DOMContentLoaded 的浏览器(如 IE < 9)中可能会有延迟(事件被触发时的时间变化)。但是我在一个肯定支持它的浏览器中测试了这个东西(Chrome 20 和 Firefox 13),在两个浏览器中都出现了约 15-20 秒的延迟。 - trejder
@trejder 抱歉,我完全没有看到任何类似的情况。请参见http://jsfiddle.net/rqTAX/3/。差异只有5-20毫秒。在Chrome和Firefox中。 - Esailija
不用道歉!:] 这只是我的好奇心。也许我错过了什么。对我来说,我认为我们可以关闭这个话题了。谢谢! - trejder
@trejder 好吧,你至少可以接受我的答案,因为我提供了大多数人不知道的顶部两个区别。 另外,你应该使用 $(document).ready 而不是 .bind,它们非常不同。 - Esailija
谢谢你提醒我,因为我很“确定”我已经采纳了你的答案!:]对不起!顺便问一下:为什么$(document).bind('ready', function() {});$(document).ready(function() {});这两个写法会有这么大的区别呢? - trejder
显示剩余2条评论

5
jQuery通过绑定到documentreadystatechange事件来模拟此事件,这是在旧版IE中模拟DOMContentLoaded的标准方式。
根据jQuery源码,该事件会“晚一些”但在window.onload之前触发。然而,我无法确定该事件确切的触发时间。当DOM构建完毕并准备好进行脚本编写时,DOMContentLoaded就会触发,因此readystatechange会在此之后触发;也许它等待布局渲染或样式等内容,或者该事件稍后在渲染/布局过程中触发?
无论如何,它很可能会在DOMContentLoaded之后触发,很可能是由于IE决定将documentreadyState更新为“complete”。
(如果有人有明确的答案,请发表评论,我会更新这个答案;我自己也很想知道它究竟在什么时候触发,但我找不到任何文档或任何我期望的网站上的答案,比如Quirksmode。)

好的,但是类似于上面。你大多数时间都在谈论IE。而且最新版本的Chrome和Firefox也会出现完全相同的延迟问题。 - trejder
DOMContentLoaded 事件发生在脚本(普通的和 defer 的)运行之后(除了 async 的)。 - Simon_Weaver

2
“ready”事件看起来在实践中延迟触发的另一个原因是可能有许多事件与其连接。如果其中任何一个是长时间运行的同步操作,则“ready”事件将会更晚出现。例如,我使用knockout.js,它需要500毫秒来初始化页面。”
然而,在使用您的最小测试页面时,当然不是这种情况。我尝试运行以下内容:
 console.log(window.performance.timing.domContentLoadedEventEnd -
             window.performance.timing.domContentLoadedEventStart);

这甚至对于您的简单超级页面也只需要约6毫秒。
我还拿了您的代码并通过Chrome的性能工具运行了一下,发现了一些有趣的事情:

enter image description here

顺便说一句:垂直蓝条是DOMCONTENTLOADED,绿色是“首次绘制”。即使只是在DOMCONTENTLOADED事件上调用一个超级简单的函数,也可能需要5毫秒(这是在i7上)。请记住,它需要被解析和初始化。回调以青色(匿名)显示,第一个来自DOMCONTENTLOADED事件,第二个是“准备就绪”回调。你会注意到“计时器已触发”(即setTimeout)。因此,这绝不是即时的。查看jQuery源代码,你会发现他们实际上为回调设置了一个计时器。
// Handle it asynchronously to allow scripts the opportunity to delay ready
window.setTimeout( jQuery.ready );

我不是很确定他们在这里的意思(你有什么想法)- 但它解释了这种行为。我可以看到它有助于避免竞态条件,并阻塞UI,但“延迟准备”对我来说还不清楚。

但我正在使用Chrome!时间可能微不足道,但我正在尝试启动一个SignalR连接,并且需要尽快完成。 - Simon_Weaver
是的,它可以工作,但由于计时器存在不必要的延迟。也许存在某种竞争条件,但我发现将我的SignalR初始化代码放在自己的DOMContentLoaded中更快,因为我只需要支持现代浏览器。 - Simon_Weaver
这很可能是由于jQuery本身的解析,它有84KB。 - Daniel T.
此外,使用开发工具来监视这个过程也很可能会减慢速度,不是吗? - Kyle Baker
@KyleBaker 这里的开发工具更多是为了展示中间发生了什么。打开工具不会影响窗口性能计时值。 - Simon_Weaver
显示剩余2条评论

1

除了我之外,还有人在使用jQuery吗?;)

jQuery 3.0开始,只推荐使用$( handler )语法,
(等同于$( document ).ready( handler ))。

来自API文档:.ready()


这会使得更难找到它的位置。我正在处理一个大型 Razor 项目,其中有许多出现 $( document ).ready(handler)). 的情况,如果有人更改了它,团队中的其他开发人员都会抱怨... - sergiol

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接