Javascript封装DOMContentLoaded事件实例


Posted in Javascript onJune 12, 2014

最近在写一个Javascript的框架,刚把DOMContentLoaded事件封装好,略带小兴奋,把开发过程中遇到的原理和兼容性问题做篇笔记,省的忘记到处找。

我们在写js代码的时候,一般都会添加window.onload事件,主要是为了在DOM加载完后可以使用getElementById,getElementsByTagName等方法选取DOM元素进行操作,但是window.load会等到加载完DOM、脚本、CSS,最后加载完图片甚至是iframe中的所有资源才会触发,很多时候网页的图片较多较大,要等最后图片这个耗时大户加载完才执行js明显有些太迟了,很多时候都会影响用户体验。

很多js框架都有个document.ready的功能,像JQuery的$(document).ready()方法,可以在DOM加载完就立即执行js代码,让图片自个慢慢加载吧。

document.ready的核心是DOMContentLoaded事件,firefox、chrome、opera、safari、ie9+都可以使用addEventListener(‘DOMContentLoaded',fn,false)进行事件绑定,而ie6~8不支持DOMContentLoaded事件,所以要针对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未加载完成时,调用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查看加载过程,每次测试前记得先清理下浏览器缓存。

Javascript 相关文章推荐
使用javascript实现页面定时跳转总结篇
Sep 21 Javascript
一个简单的jquery进度条示例
Apr 28 Javascript
BootStrap Progressbar 实现大文件上传的进度条的实例代码
Jun 27 Javascript
AngularJS基础 ng-href 指令用法
Aug 01 Javascript
jQuery简单创建节点的方法
Sep 09 Javascript
PHP抓取HTTPS内容和错误处理的方法
Sep 30 Javascript
jquery中用jsonp实现搜索框功能
Oct 18 Javascript
Angularjs实现下拉框联动的示例代码
Aug 22 Javascript
基于 D3.js 绘制动态进度条的实例详解
Feb 26 Javascript
JS双向链表实现与使用方法示例(增加一个previous属性实现)
Jan 31 Javascript
使用Vue.js 和Chart.js制作绚丽多彩的图表
Jun 15 Javascript
VUE路由动态加载实例代码讲解
Aug 26 Javascript
自己封装的javascript事件队列函数版
Jun 12 #Javascript
jquery动态添加删除一行数据示例
Jun 12 #Javascript
checkbox勾选判断代码分析
Jun 11 #Javascript
百度判断手机终端并自动跳转js代码及使用实例
Jun 11 #Javascript
js获取日期:昨天今天和明天、后天
Jun 11 #Javascript
js使用栈来实现10进制转8进制与取除数及余数
Jun 11 #Javascript
删除javascript中注释语句的正则表达式
Jun 11 #Javascript
You might like
WINXP下apache+php4+mysql
2006/11/25 PHP
php图像处理函数大全(推荐收藏)
2013/07/11 PHP
thinkphp的CURD和查询方式介绍
2013/12/19 PHP
用脚本调用样式的几种方法
2006/12/09 Javascript
JQuery浮动DIV提示信息并自动隐藏的代码
2010/08/29 Javascript
jquery lazyload延迟加载技术的实现原理分析
2011/01/24 Javascript
js自定义事件及事件交互原理概述(二)
2013/02/01 Javascript
jQuery阻止同类型事件小结
2013/04/19 Javascript
纯javascript判断查询日期是否为有效日期
2015/08/24 Javascript
用JavaScript判断CSS浏览器类型前缀的两种方法
2015/10/08 Javascript
js使用cookie记录用户名的方法
2015/11/26 Javascript
Node.js的Express框架使用上手指南
2016/03/12 Javascript
如何清除IE10+ input X 文本框的叉叉和密码输入框的眼睛图标
2016/12/21 Javascript
Bootstrap模态框案例解析
2017/03/05 Javascript
大白话讲解JavaScript的Promise
2017/04/06 Javascript
老生常谈combobox和combotree模糊查询
2017/04/17 Javascript
vue 添加vux的代码讲解
2017/11/30 Javascript
javascript变量提升和闭包理解
2018/03/12 Javascript
jQuery选择器选中最后一个元素,倒数第二个元素操作示例
2018/12/10 jQuery
layui时间控件选择时间范围的实现方法
2019/09/28 Javascript
十分钟教你上手ES2020新特性
2020/02/12 Javascript
vue+flask实现视频合成功能(拖拽上传)
2021/03/04 Vue.js
利用python实现数据分析
2017/01/11 Python
用Pygal绘制直方图代码示例
2017/12/07 Python
新手常见6种的python报错及解决方法
2018/03/09 Python
numpy中的delete删除数组整行和整列的实例
2018/05/09 Python
python实现图书借阅系统
2019/02/20 Python
树莓派使用USB摄像头和motion实现监控
2019/06/22 Python
pip install python 快速安装模块的教程图解
2019/10/08 Python
python hash每次调用结果不同的原因
2019/11/21 Python
通过实例了解Python异常处理机制底层实现
2020/07/23 Python
日本高端护肤品牌:Tatcha
2016/08/29 全球购物
社区学习雷锋活动总结
2014/04/25 职场文书
支部书记四风对照材料
2014/08/28 职场文书
小学数学教学反思范文
2016/02/16 职场文书
Pycharm连接远程服务器并远程调试的全过程
2021/06/24 Python