JavaScript封装DOMContentLoaded事件的原理与兼容实现
本文深入解析JavaScript中DOMContentLoaded事件的封装原理,重点解决IE6~8不支持该事件的问题。通过doScroll方法判断DOM加载状态,并结合事件队列机制确保多个回调有序执行,提供完整兼容代码示例。
DOMContentLoaded事件封装概述
近期在开发JavaScript框架时完成了DOMContentLoaded事件的封装,特此记录开发过程中的核心原理及兼容性问题,以便后续查阅。
编写JS代码时,通常通过window.onload事件确保DOM加载完成后使用getElementById或getElementsByTagName等方法操作元素。然而,window.onload需等待DOM、脚本、CSS及图片、iframe等所有资源加载完毕才触发。若页面图片较多或体积较大,JS执行会被显著延迟,影响用户体验。
多数JS框架提供document.ready功能,例如jQuery的$(document).ready()方法,允许在DOM加载完成后立即执行JS代码,而无需等待图片等资源加载。
现代浏览器与IE6-8的兼容处理
document.ready的核心机制依赖于DOMContentLoaded事件。Firefox、Chrome、Opera、Safari及IE9+均支持通过addEventListener('DOMContentLoaded', fn, false)绑定事件。由于IE6~8不支持此事件,需针对这些版本进行兼容性处理。
据资料,IE6~8可通过document.onreadystatechange事件监听document.readyState状态是否为complete来判断DOM加载状态。但若页面包含iframe,IE6~8的document.readyState需等待iframe内所有资源加载完毕才会变为complete,此时iframe成为性能瓶颈。实测发现,即使无iframe,当readyState为complete时,实际触发的是onload而非DOMContentLoaded,这一现象值得注意。
IE浏览器特有的doScroll方法可用于判断DOM加载状态:当DOM未加载完成时调用doScroll会报错;反之,若持续间隔调用doScroll直至不报错,则表明DOM已加载完毕。该方法不受图片及iframe内容加载状态影响,具有可靠性。
若多个JS文件绑定document.ready事件,为防止浏览器重复绑定并确保执行顺序,可引入事件队列机制。
上述内容总结了document.ready事件的原理及兼容性问题。下文提供实例代码,采用函数封装模式,执行过程已详细注释,欢迎指正。
完整封装代码实现
代码如下:
//保存domReady的事件队列
eventQueue = [];
//判断DOM是否加载完毕
isReady = false;
//判断DOMReady是否绑定
isBind = false;
/*执行domReady()
*
*@param {function}
*@execute 将事件处理程序压入事件队列,并绑定DOMContentLoaded
* 如果DOM加载已经完成,则立即执行
*@caller
*/
function domReady(fn){
if (isReady) {
fn.call(window);
}
}else{
eventQueue.push(fn);
};
bindReady();
};
/*domReady事件绑定
*
*@param null
*@execute 现代浏览器通过addEvListener绑定DOMContentLoaded,包括ie9+
ie6-8通过判断doScroll判断DOM是否加载完毕
*@caller domReady()
*/
function bindReady(){
if (isReady) return;
if (isBind) return;
isBind = true;
if (window.addEventListener) {
document.addEventListener('DOMContentLoaded',execFn,false);
}
}else if (window.attachEvent) {
doScroll();
};
};
/*doScroll判断ie6-8的DOM是否加载完成
*
*@param null
*@execute doScroll判断DOM是否加载完成
*@caller bindReady()
*/
function doScroll(){
try{
document.documentElement.doScroll('left');
}
}catch(error){
return setTimeout(doScroll,20);
};
}execFn();
};
/*执行事件队列
*
*@param null
*@execute 循环执行队列中的事件处理程序
*@caller bindReady()
*/
function execFn(){
if (!isReady) {
isReady = true;
for (var i = 0; i < eventQueue.length; i++) {
eventQueue[i].call(window);
};
}eventQueue = [];
};
};
//js文件1
domReady(function(){
...
});
//js文件2
domReady(function(){
...
});
//注意,如果是异步加载的js就不要绑定domReady方法,不然函数不会执行,
//因为异步加载的js下载之前,DOMContentLoaded已经触发,addEventListener执行时已经监听不到了
测试页面:都加载了两张很大的图片,onload需要图片加载完才能执行js,DOMContentLoaded只需等到DOM加载完即可执行js。可以打开firebug查看加载过程,每次测试前记得先清理下浏览器缓存。
D3.js 处理大规模数据点的性能优化策略
2026-11-05 15:37:25
JS实现网页摄像头实时扫码:结合getUserMedia与jsQR教程
2026-11-05 15:27:28
D3.js v5 迁移到 v7 的 API 变更与注意事项
2026-11-05 11:57:17
使用 animate.js 实现多元素协同的序列动画
2026-11-04 22:27:53
JS拖拽排序时怎么添加平滑动画效果?FLIP技术实战
2026-11-04 10:12:22
