我们在开发时,经常需要检测页面是否加载完毕,以确保脚本安全运行,下面我们就来浅谈一下检测页面是否加载完毕的那些事件们。
1. onload 事件
在页面的所有资源加载完成时,window对象上会触发一个onload
事件。该事件通常被用以执行一些逻辑代码。比如,你需要通过JS去访问一个DOM。
<script>
console.log(document.getElementById('name').innerHTML);
</script>
<div id="name">chengxuyuan</div>
上述代码运行时肯定会报错,因为脚本执行时,id为name的div
还没有加载完成。那么什么时机才是我们获取DOM文档的可靠时机呢?正是我们上面说道的onload
,页面的onload
触发时,证明页面文档流及资源已经完全加载完毕,此时,获取在文档流中的DOM是最“安全”的时机。我们将上述代码加以改造,如下:
<script>
window.onload = function () {
console.log(document.getElementById('myname').innerHTML);
}
</script>
<div id="myname">chengxuyuan</div>
再次运行时,代码便不会报错了。因此,onload
事件的实际效果是当页面解析完DOM树,并且完成了所有图片、样式表、脚本等资源的加载后才被触发。那么问题来了,当资源过多过大时,onload
会出现比较严重的延迟问题,严重影响用户体验。
2. DOMContentLoaded 事件
对比上述情况,Firefox的DOMContentLoaded
事件就更加合理,该方法触发的时间更早,它在DOM内容加载完后就触发,无需等待其他资源的加载完成。
<script>
window.onload = function () {
console.log('页面资源全部加载完毕');
}
document.addEventListener("DOMContentLoaded", function(event) {
console.log('DOM已被完全加载和解析');
});
</script>
上述代码的执行结果为依次打印出:
DOM已被完全加载和解析
页面资源全部加载完毕
由此可见,DOMContentLoaded
事件能更早地捕获到DOM加载完成。
目前,Webkit 525以上版本和Opera也包含该方法。此外,它目前已在HTML5中被标准化。但IE仍不支持DOMContentLoaded
。
另外,很多JavaScript框架都有document.ready功能,例如jQuery的:
$(document).ready(function(){});
它的核心就是DOMContentLoaded
事件,可以使用:
document.addEventListener("DOMContentLoaded",function(){...},false);
进行事件绑定,但还是需要针对IE做兼容性处理。
3. onreadystatechange 事件
虽然IE不支持DOMContentLoaded
,但它支持onreadystatechange
事件,该事件的目的是提供与文档或元素的加载状态有关的信息。支持onreadystatechange
事件的每个对象都有一个readyState
属性,可能包含下列5个值中的一个。
uninitialized
(为初始化):对象存在但尚未初始化。loading
(正在加载):对象正在加载数据。loaded
(加载完毕):对象加载数据完成。interactive
(交互):可以操作对象了,但还没有完全加载。complete
(完成):对象已经加载完毕。
onreadystatechange
事件可以用于检测DOM是否加载完毕,当document.readyState == 'complete'
时,表示DOM加载完成。但是如果页面中有iframe
的话,会等到iframe
中的所有资源加载完才会变成complete
。 此时也造成了主页面的延迟。并且,经测试,即使页面中没有iframe
, 该方式也与onload
相当,依然会等到所有资源下载完毕后才触发。
4. doScroll方法
不过,IE还有个特有的方法doScroll
, 通过间隔调用:
document.documentElement.doScroll("left");
可以检测DOM是否加载完成。 当页面未加载完成时,该方法会报错,直到doScroll
不再报错时,就代表DOM加载完成了。该方法更接近DOMContentLoaded
的实现。
5. Javascript封装DOMContentLoaded事件
以下,是JS封装DOMContentLoaded
事件从而达到良好的兼容性的一个简单的代码实现。
function ready(fn){
// 目前Mozilla、Opera和webkit 525+内核支持DOMContentLoaded事件
if(document.addEventListener) {
document.addEventListener('DOMContentLoaded', function() {
document.removeEventListener('DOMContentLoaded',arguments.callee, false);
fn();
}, false);
}
// 如果IE
else if(document.attachEvent) {
// 确保当页面是在iframe中加载时,事件依旧会被安全触发
document.attachEvent('onreadystatechange', function() {
if(document.readyState == 'complete') {
document.detachEvent('onreadystatechange', arguments.callee);
fn();
}
});
// 如果是IE且页面不在iframe中时,轮询调用doScroll 方法检测DOM是否加载完毕
if(document.documentElement.doScroll && typeof window.frameElement === "undefined") {
try{
document.documentElement.doScroll('left');
}
catch(error){
return setTimeout(arguments.callee, 20);
};
fn();
}
}
};
对于IE,首先注册document
的onreadystatechange
事件,这是为了避免当页面处于iframe
中时,doScroll
方法会失效,因此在实现代码中做了判断。之后,判断如果是IE并且页面不在iframe
当中, 则通过setTimeout
来不断的调用:
document.documentElement.doScroll('left');
直到调用成功,代表DOM加载完成。
总结一下,开发时我们可以通过封装DOMContentLoaded
事件来检测页面DOM是否加载完毕,然后执行逻辑代码,提升用户体验。