我有一个带有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 beta上 (但使用{{ 1}}它没有)。去图。
OH伟大的JAVASCRIPT和DOM,祈祷显示我的方式的错误!
答案 0 :(得分:55)
通过异步加载脚本,您告诉浏览器它可以独立于页面的其他部分加载该脚本。这意味着页面可能已完成加载,并且可能在加载脚本之前和注册事件之前触发DOMContentLoaded
。如果发生这种情况,您将错过该事件(当您注册时已经发生了这一事件。)
在某些浏览器中,您可以测试文档以查看它是否已加载。我没有检查所有浏览器兼容性,但在Firefox 3.6+(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 );
}
答案 1 :(得分:23)
这不是最终的答案,但让我理解为什么使用异步与需要修改DOM的脚本不正确,所以必须等待DOMContentLoaded事件。希望可能是有益的。
答案 2 :(得分:3)
解决此问题的一种方法是在窗口对象上使用load事件。
这将比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");
});
答案 3 :(得分:2)
大多数vanilla JS Ready函数在文档已经加载后不考虑DOMContentLoaded
处理程序设置的情况 - 这意味着该函数永远不会运行。如果您在DOMContentLoaded
外部脚本(async
)中查找<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!
});
如果你想支持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!
});