【问题标题】:Async-loaded scripts with DOMContentLoaded or load event handlers not being called?带有 DOMContentLoaded 的异步加载脚本或未调用加载事件处理程序?
【发布时间】:2021-08-07 13:41:57
【问题描述】:

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

document.addEventListener('DOMContentLoaded', function() {
    console.log('Hi');
});

我正在异步加载——

<script async src=script.js></script>

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

<script src=script.js></script>

效果很好。

(即使我将 DOMContentLoaded 事件更改为 load 事件,它也永远不会被调用。)

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

编辑:它不适用于 Chrome 18.0.1025.11 测试版,但使用DOMContentLoaded,它可以在 Firefox 11 测试版上运行(但使用 load 它没有)。去图吧。

哦,伟大的 JAVASCRIPT 和 DOM 之王,请向我展示我的方式的错误!

【问题讨论】:

  • 通过异步加载脚本,您告诉浏览器它可以独立于页面的其他部分加载该脚本。这意味着页面可能会完成加载,并且可能会在您的脚本加载之前和注册事件之前触发 DOMContentLoaded。如果发生这种情况,您将错过该活动(在您注册时已经发生)。
  • 哦——有趣。所以我真的应该测试已经触发的事件——(edit:实际上,因为我依赖于触发脚本之后的事件,所以它不应该是异步的全部)。凉爽的。把它写成答案,你就可以投票了:)

标签: javascript dom onload


【解决方案1】:

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

在所有现代浏览器中,您可以测试文档以查看它是否已经加载(MDN doc),您可以检查:

if (document.readyState !== "loading")

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

事实上,作为参考源和实现思路,jQuery 用它的.ready() 方法做了同样的事情,并且看起来得到了广泛的支持。当调用.ready() 时,jQuery 有这个代码,它首先检查文档是否已经加载。如果是这样,它会立即调用 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 );
}

【讨论】:

  • 是的,先生,我只需要知道这些。我在投票上撒了谎——没有足够的代表;)我希望有人好心为你投票:)
  • 如果document.readyState 是 Gecko-only:解决方法是运行另一个 DOMContentLoaded 事件处理程序同步,它将在运行时设置一个全局标志,我们可以事后检查 :) 编辑:哦,等待所有酷炫的浏览器都支持它,这样我们就可以了
  • 试一试,结果发现检查document.readyState == 'complete'不够 - 当检查发生时,document.readyStateinteractive。检查document.readyState != 'loading' 工作正常:)
  • 从我读到的WHATWG's draft(向上滚动一点)- 在文档加载时返回“正在加载”,在完成解析后返回“交互”,但仍在加载子资源,并在加载后“完成”。 (非规范)。最后我检查了document.readyState == 'loading' 并在其中注册了事件处理程序
  • @user1203233 - 我知道这是旧的,但为了其他人的利益,我会添加此评论。在 IE 的某些版本中使用 document.readyState == 'loading' 相关的 jQuery 开发过程中存在 cmets 和 bug。它根本不起作用(它触发得太早)。因此 jQuery 只使用document.readyState === 'complete'。这些错误不容易重现,因为它们仅在特定服务器发送缓冲区大小和特定大小文档的特定情况下发生。但是,它可以被复制并且是 IE 中的一个真正的错误。
【解决方案2】:

这不是最终答案,但让我明白了为什么将异步与需要修改 DOM 的脚本一起使用是不正确的,因此必须等待 DOMContentLoaded 事件。希望可能是有益的。

(来源:Running Your Code at the Right Time from kirupa.com

【讨论】:

    【解决方案3】:

    大多数 vanilla JS Ready 函数不考虑DOMContentLoaded 处理程序在文档已经加载后启动的场景 - 这意味着该函数将永远不会运行。如果您在 async 外部脚本 (&lt;script async src="file.js"&gt;&lt;/script&gt;) 中使用 DOMContentLoaded,就会发生这种情况。

    仅当文档的readyState 不是interactivecomplete 时,下面的代码才会检查DOMContentLoaded

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

    如果你也想支持 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!
    });
    

    【讨论】:

    • 您也可以使用 document.readyState !== "loading" 代替 OR (values)
    【解决方案4】:

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

    这将在 DOMContentLoaded 之后发生,但至少您不必担心错过事件。

    window.addEventListener("load", function () {
       console.log('window loaded');
    });
    

    如果你真的需要捕捉 DOMContentLoaded 事件,你可以使用 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");
    });
    

    【讨论】:

      猜你喜欢
      • 2012-10-01
      • 1970-01-01
      • 2011-12-04
      • 1970-01-01
      • 1970-01-01
      • 2013-04-02
      • 2015-02-01
      • 2021-10-24
      • 1970-01-01
      相关资源
      最近更新 更多