javascript 带有 DOMContentLoaded 的异步加载脚本或未调用加载事件处理程序?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9237044/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Async-loaded scripts with DOMContentLoaded or load event handlers not being called?
提问by user1203233
I've got a script with a DOMContentLoaded
event handler—
我有一个带有DOMContentLoaded
事件处理程序的脚本——
document.addEventListener('DOMContentLoaded', function() {
console.log('Hi');
});
Which I'm loading asynchronously—
我正在异步加载——
<script async src=script.js></script>
However, the event handler is never called. If I load it synchronously—
但是,从不调用事件处理程序。如果我同步加载——
<script src=script.js></script>
It works fine.
它工作正常。
(Even if I change the DOMContentLoaded
event to a load
event, it's never called.)
(即使我将DOMContentLoaded
事件更改为load
事件,它也永远不会被调用。)
What gives? The event handler should be registered irrespective of how the script is loaded by the browser, no?
是什么赋予了?无论浏览器如何加载脚本,都应该注册事件处理程序,不是吗?
Edit: It doesn't work on Chrome 18.0.1025.11 beta but, with DOMContentLoaded
, it doeson Firefox 11 beta (but with load
it doesn't). Go figure.
编辑:它无法在Chrome 18.0.1025.11测试工作,但,有DOMContentLoaded
,它不会在Firefox 11 Beta版(但load
事实并非如此)。去搞清楚。
OH GREAT LORDS OF JAVASCRIPT AND THE DOM, PRAY SHOW THE ERROR OF MY WAYS!
哦伟大的 JAVASCRIPT 和 DOM 领主,请指出我的方式的错误!
回答by jfriend00
By loading the script asynchronously, you are telling the browser that it can load that script independently of the other parts of the page. That means that the page may finish loading and may fire DOMContentLoaded
BEFORE your script is loaded and before it registers for the event. If that happens, you will miss the event (it's already happened when you register for it).
通过异步加载脚本,您告诉浏览器它可以独立于页面的其他部分加载该脚本。这意味着页面可能会完成加载,并且可能会DOMContentLoaded
在加载脚本之前和注册事件之前触发。如果发生这种情况,您将错过该活动(注册时已经发生了)。
In some browsers, you can test the document to see if it's already loaded. I haven't checked all the browser compatibility, but in Firefox 3.6+ (MDN doc), you can check:
在某些浏览器中,您可以测试文档以查看它是否已加载。我还没有检查所有浏览器的兼容性,但是在 Firefox 3.6+ ( MDN doc) 中,您可以检查:
if (document.readyState !== "loading")
to see if the document is already loaded. If it is, just do your business. If it's not, then install your event listener.
查看文档是否已加载。如果是,就做你的事。如果不是,则安装您的事件侦听器。
In fact, as a reference source and implementation idea, jQuery does this very same thing with it's .ready()
method and it looks widely supported. jQuery has this code when .ready()
is called that first checks to see if the document is already loaded. If so, it calls the ready function immediately rather than binding the event listener:
事实上,作为参考源和实现思想,jQuery 用它的.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 );
}
回答by Manuel Solorzano
This is not the final answer but made me understand why is not correct using async with a script that need to modify DOM, so must wait to DOMContentLoaded event. Hope could be beneficial.
这不是最终答案,但让我明白为什么将异步与需要修改 DOM 的脚本一起使用是不正确的,因此必须等待 DOMContentLoaded 事件。希望可能是有益的。
(Source: Running Your Code at the Right Time from kirupa.com)
回答by Null
Most vanilla JS Ready functions do NOT consider the scenario where the DOMContentLoaded
handler is initiated afterthe document already has loaded - Which means the function will never run. This can happen if you use DOMContentLoaded
within an async
external script (<script async src="file.js"></script>
).
大多数 vanilla JS Ready 函数不考虑在 文档已经加载之后DOMContentLoaded
启动处理程序的情况——这意味着函数永远不会运行。如果您在外部脚本 ( ) 中使用,则可能会发生这种情况。DOMContentLoaded
async
<script async src="file.js"></script>
The code below checks for DOMContentLoaded
only if the document's readyState
isn't already interactive
or complete
.
下面的代码DOMContentLoaded
仅检查文档readyState
是否已经存在interactive
或complete
.
var DOMReady = function(callback) {
document.readyState === "interactive" || document.readyState === "complete" ? callback() : document.addEventListener("DOMContentLoaded", callback);
};
DOMReady(function() {
//DOM ready!
});
If you want to support IE aswell:
如果您还想支持 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!
});
回答by user4617883
One way around this is to use the load event on the window object.
解决此问题的一种方法是在 window 对象上使用 load 事件。
This will happen later than DOMContentLoaded, but at least you don't have to worry about missing the event.
这会晚于 DOMContentLoaded 发生,但至少您不必担心错过该事件。
window.addEventListener("load", function () {
console.log('window loaded');
});
If you really need to catch DOMContentLoaded event you can do use Promise object. Promise will get resolved even if it happened earlier:
如果你真的需要捕捉 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");
});