[英]Async-loaded scripts with DOMContentLoaded or load event handlers not being called?
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 does on Firefox 11 beta (but with load
it doesn't).编辑:它无法在Chrome工作18.0.1025.11测试阶段,但与DOMContentLoaded
,它在Firefox 11 Beta版(但load
事实并非如此)。 Go figure.去搞清楚。
OH GREAT LORDS OF JAVASCRIPT AND THE DOM, PRAY SHOW THE ERROR OF MY WAYS!哦伟大的 JAVASCRIPT 和 DOM 领主,请指出我的方式的错误!
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.这意味着页面可能会完成加载,并且可能会在加载脚本之前和注册事件之前触发DOMContentLoaded
。 If that happens, you will miss the event (it's already happened when you register for it).如果发生这种情况,您将错过该活动(注册时已经发生了)。
In all modern browsers, you can test the document to see if it's already loaded ( MDN doc ), you can check:在所有现代浏览器中,您可以测试文档以查看它是否已加载( 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 用它的.ready()
方法做了同样的事情,它看起来得到了广泛的支持。 jQuery has this code when .ready()
is called that first checks to see if the document is already loaded.当.ready()
被调用时,jQuery 有这个代码,它首先检查文档是否已经加载。 If so, it calls the ready function immediately rather than binding the event listener:如果是这样,它会立即调用就绪函数而不是绑定事件侦听器:
// 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 );
}
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.这不是最终答案,但让我明白为什么将异步与需要修改 DOM 的脚本一起使用是不正确的,因此必须等待 DOMContentLoaded 事件。 Hope could be beneficial.希望可能是有益的。
(Source: Running Your Code at the Right Time from kirupa.com ) (来源: 在正确的时间运行您的代码来自 kirupa.com )
Most vanilla JS Ready functions do NOT consider the scenario where the DOMContentLoaded
handler is initiated after the document already has loaded - Which means the function will never run .大多数 vanilla JS Ready 函数不考虑在文档已经加载之后启动DOMContentLoaded
处理程序的情况——这意味着该函数将永远不会运行。 This can happen if you use DOMContentLoaded
within an async
external script ( <script async src="file.js"></script>
).如果您在async
外部脚本 ( <script async src="file.js"></script>
) 中使用DOMContentLoaded
,则可能会发生这种情况。
The code below checks for DOMContentLoaded
only if the document's readyState
isn't already interactive
or complete
.下面的代码仅在文档的readyState
尚未interactive
或complete
检查DOMContentLoaded
。
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!
});
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.如果你真的需要捕捉 DOMContentLoaded 事件,你可以使用 Promise 对象。 Promise will get resolved even if it happened earlier: 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");
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.