没有调用带有DOMContentLoaded或加载事件处理程序的异步加载脚本?

use*_*233 33 javascript dom onload

我有一个带有DOMContentLoaded事件处理程序的脚本 -

document.addEventListener('DOMContentLoaded', function() {
    console.log('Hi');
});
Run Code Online (Sandbox Code Playgroud)

我正异步加载 -

<script async src=script.js></script>
Run Code Online (Sandbox Code Playgroud)

但是,永远不会调用事件处理程序.如果我同步加载它 -

<script src=script.js></script>
Run Code Online (Sandbox Code Playgroud)

它工作正常.

(即使我将DOMContentLoaded事件更改为load事件,也从未调用过.)

是什么赋予了?无论浏览器如何加载脚本,都应该注册事件处理程序,不是吗?

编辑:它无法在Chrome 18.0.1025.11测试工作,但,有DOMContentLoaded,它不会在Firefox 11 Beta版(但load事实并非如此).去搞清楚.

哦伟大的JAVASCRIPT和DOM,祈祷显示我的方式的错误!

jfr*_*d00 57

通过异步加载脚本,您告诉浏览器它可以独立于页面的其他部分加载该脚本.这意味着页面可能已完成加载,并且可能DOMContentLoaded在加载脚本之前和注册事件之前触发.如果发生这种情况,您将错过事件(当您注册时已经发生了).

在某些浏览器中,您可以测试文档以查看它是否已加载.我没有检查所有浏览器兼容性,但在Firefox 3.6+(MDN doc)中,您可以检查:

if (document.readyState !== "loading")
Run Code Online (Sandbox Code Playgroud)

查看文档是否已加载.如果是,那就做你的事.如果不是,则安装您的事件监听器.

事实上,作为一个参考源和实现的想法,jQuery使用它的.ready()方法做了同样的事情,它看起来广泛支持.jQuery在.ready()调用时会调用此代码,首先检查文档是否已加载.如果是这样,它立即调用ready函数而不是绑定事件监听器:

// Catch cases where $(document).ready() is called after the
// browser event has already occurred.
if ( document.readyState === "complete" ) {
    // Handle it asynchronously to allow scripts the opportunity to delay ready
    return setTimeout( jQuery.ready, 1 );
}
Run Code Online (Sandbox Code Playgroud)

  • 尝试一下,结果检查`document.readyState =='complete'`是不够的* - 当检查发生时,`document.readyState`是`interactive`.检查`document.readyState!='loading'`可以正常工作:) (7认同)
  • 从我读到的[WHATWG草案](http://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#dom-document-readystate)(向上滚动) - *在文档加载时返回"加载",在完成解析但仍加载子资源时返回"交互",并在加载后"完成".(非标准)*.最后,我检查了`document.readyState =='loading'`并在其中注册了事件处理程序 (3认同)
  • @ user1203233 - 我知道这已经过时了,但为了别人的利益,我会添加这条评论.在某些版本的IE中使用`document.readyState =='loading'`的jQuery开发过程中有一些注释和错误.它根本不起作用(太火了).因此jQuery只使用`document.readyState ==='complete'`.这些错误不容易重现,因为它们仅在某些服务器发送缓冲区大小和特定大小的文档的某些情况下发生.但是,它可以被复制,并且是IE中的一个真正的错误. (2认同)

man*_*nus 23

这不是最终的答案,但让我理解为什么使用异步与需要修改DOM的脚本是不正确的,所以必须等待DOMContentLoaded事件.希望可能是有益的.

在此输入图像描述

(来源:从kirupa.com在正确的时间运行您的代码)


use*_*883 5

解决这个问题的一种方法是使用窗口对象上的加载事件。

这将晚于 DOMContentLoaded 发生,但至少您不必担心错过该事件。

window.addEventListener("load", function () {
   console.log('window loaded');
});
Run Code Online (Sandbox Code Playgroud)

如果你确实需要捕获 DOMContentLoaded 事件,你可以使用 Promise 对象。即使它发生得更早,Promise 也会得到解决:

HTMLDocument.prototype.ready = new Promise(function (resolve) {
if (document.readyState != "loading")
    return resolve();
else
    document.addEventListener("DOMContentLoaded", function () {
        return resolve();
    });
});

document.ready.then(function () {
    console.log("document ready");
});
Run Code Online (Sandbox Code Playgroud)


Mal*_*voz 5

大多数香草JS Ready函数不会考虑在 文档已加载DOMContentLoaded启动处理程序的情况-这意味着该函数将永远不会运行。如果在外部脚本()中使用,可能会发生这种情况。DOMContentLoadedasync<script async src="file.js"></script>

以下代码DOMContentLoaded仅在文档readyState不是interactive或时检查complete

var DOMReady = function(callback) {
  document.readyState === "interactive" || document.readyState === "complete" ? callback() : document.addEventListener("DOMContentLoaded", callback());
};
DOMReady(function() {
  //DOM ready!
});
Run Code Online (Sandbox Code Playgroud)

如果您也要支持IE:

var DOMReady = function(callback) {
    if (document.readyState === "interactive" || document.readyState === "complete") {
        callback();
    } else if (document.addEventListener) {
        document.addEventListener("DOMContentLoaded", callback());
    } else if (document.attachEvent) {
        document.attachEvent("onreadystatechange", function() {
            if (document.readyState != "loading") {
                callback();
            }
        });
    }
};

DOMReady(function() {
  // DOM ready!
});
Run Code Online (Sandbox Code Playgroud)