在 JavaScript 的传统事件模型中,load 是页面中最早被触发的事件。不过当使用 load 事件来初始化页面时可能会存在一个问题,就是当页面中包含很大的文件时,load 事件需要等到所有图像全部载入完成之后才会被触发。也许用户希望某些脚本能够在页面结构加载完毕之后就能够被执行。要怎么办呢?
这时可以考虑使用 DOMContentLoaded 事件类型。作为 DOM 标准事件,它是在 DOM 文档结构加载完毕的时候触发的,因此要比 load 事件类型先被触发。目前,Mozilla 和 Opera 新版本已经支持了该事件。而 IE 和 Safari 浏览器还不支持。
如果在标准 DOM 中,可以这样设计。
<script>
window.onload = f1;
if (document.addEventListener) {
document.addEventListener ("DOMContentLoaded", f, false);
}
function f () { alert("我要提前执行了"); }
function f1 () { alert("页面初始化完毕"); }
</script>
<img src="Winter.jpg">
这样,在图片加载之前会弹出“我要提前执行了”的提示信息,而当图片加载完毕之后会弹出“页面初始化完毕”。这说明在页面 HTML 结构加载完毕之后触发 DOMContentLoaded 事件类型,也就是说,在文档标签加载完毕时触发该事件并调用函数 f(),然后,当文档所有内容加载完毕(包括图片下载完毕)时才触发 load 事件类型,并调用函数 f1()。
由于 IE 事件模型不支持 DOMContentLoaded 事件类型,为了实现兼容处理,需要运用一点小技巧,即在文档中写入一个新的 script 元素,但是该元素会延迟到文件最后加载。然后,使用 Script 对象的 onreadystatechange 方法进行类似的 readyState 检查后及时调用载入事件。
if (window.ActiveXObject) { //兼容IE事件模型
document.write ("<script id=ie_onload defer src=javascript:void(0)><\/script"); //写入脚本标签
document.getElementById("ie_load").onreadystatechange = function () {
//判断脚本标签的状态
if (this.readyState == "complete") { //如果状态为完成,则说明文档结构加载已完毕
this.onreadystatechange = null; //清空当前反腐
f(); //调用预先执行的回调函数
}
}
}
写入的 <script> 标签中包含了 defer属性,defer 表示“延期”的意思,使用 defer 属性可以让脚本在整个页面装载完成之后在解析,而非边加载边解析。这对于只包含事件触发的脚本来说,可以提高整个页面的加载速度。与 src 属性联合使用,还可以使这些脚本在后台被下载,而前台的内容正常显示给用户。目前只有 IE 事件模型支持该属性。当定义了 defer 属性后,<script> 标签中就不应包含 document.write 命令,因为 document.write 将产生直接输出的效果,而且不包含任何立即执行脚本要使用的全局变量或者函数。
<script> 标签在文档结构加载完毕之后才加载,于是,只要判断它的状态就可以确定当前文档结构是否已经加载完毕并触发响应的事件。
针对 Safari 浏览器,可以使用 setInterval() 函数周期性的检查 document 对象的 readyState 属性,随时监控文档是否加载完毕,如果完成则调用回调函数。
if (/WebKit/i.test(navigator.userAgent)) { //兼容Safari浏览器
var _timer = setInterval (function () { //定义时间监测器
if (/loaded|complete/.test(document.readyState) { //如果当前状态显示完成
clearInterval(_timer); //清空时间监测器
f(); //调用预先执行的回调函数
}
}, 10);
}
把上面 3 段条件合并在一起即可实现兼容不同浏览器的 DomContentLoaded 事件处理函数。