深入理解Javascript封装DOMContentLoaded事件

最近在写一个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()
 * www.111cn.net
 *@param    {function}
 *@execute  将事件处理程序压入事件队列,并绑定DOMContentLoaded
 *          如果DOM加载已经完成,则立即执行
 *@caller
 */
function domReady = function(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 = function(){
    if (isReady) return;
    if (isBind) return;
    isBind = true;

    if (window.addEventListener) {
        document.addEventListener(‘DOMContentLoaded‘,execFn,false);
    }
    else if (window.attachEvent) {
        doScroll();
    };
}; www.111Cn.net

/*doScroll判断ie6-8的DOM是否加载完成
 *
 *@param    null
 *@execute  doScroll判断DOM是否加载完成
 *@caller   bindReady()
 */
function doScroll = function(){
    try{
        document.documentElement.doScroll(‘left‘);
    }
    catch(error){
        return setTimeout(doScroll,20);
    };
    execFn();
};

/*执行事件队列
 *
 *@param    null
 *@execute  循环执行队列中的事件处理程序
 *@caller   bindReady()
 */
function execFn = function(){
    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查看加载过程,每次测试前记得先清理下浏览器缓存。

onload 例子

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>onload</title>
<style type="text/css">
.div{width: 200px;height: 200px;background: red;}
body{background: url(‘bg.jpg‘) no-repeat 0 0;}
</style> www.111cn.net
<!--script type="text/javascript" >
 function loadscript(url){
  var head=document.getElementsByTagName(‘head‘)[0];
  var s=document.createElement(‘script‘);
  s.type=‘text/javascript‘;
  s.src=url;
  s.async=false;
  head.appendChild(s);
 }
 loadscript(‘go.js‘);
 loadscript(‘onload2.js‘);
</script-->
<script type="text/javascript" src="go.js"></script>
<script type="text/javascript">
window.onload=function(){
 $(‘.div‘).mousehover(function(){
  $(this).css(‘background‘,‘green‘);
 },function(){
  $(this).css(‘background‘,‘red‘);
 });
};
</script>
</head>
<body>
 <div class="div">只有等到图片加载完,鼠标移到我身上才会变色呢。<br/><br/><br/><br/><br/>(因为所用框架不支持ie6,请用ie8+测试)</div>
 <img src="bg.jpg">
 <img src="bg2.jpg">
</body>
</html>

  DOMContentLoaded例子

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>DOMContentLoaded</title>
<style type="text/css">
.div{width: 200px;height: 200px;background: red;}
body{background: url(‘bg.jpg‘) no-repeat 0 0;}
</style>
<!--script type="text/javascript" >
 function loadscript(url){
  var head=document.getElementsByTagName(‘head‘)[0];
  var s=document.createElement(‘script‘);
  s.type=‘text/javascript‘;
  s.src=url;
  s.async=false;
  head.appendChild(s);
 }
 loadscript(‘go.js‘);
 loadscript(‘onload2.js‘);
</script-->
<script type="text/javascript" src="go.js"></script>
<script type="text/javascript">
$(function(){
 $(‘.div‘).mousehover(function(){
  $(this).css(‘background‘,‘green‘);
 },function(){
  $(this).css(‘background‘,‘red‘);
 });
})
</script>
</head>
<body>
 <div class="div">在图片还没加载完之前,把鼠标移到我身上会变色哦.<br/><br/><br/><br/><br/>(因为所用框架不支持ie6,请用ie8+测试)</div>
 <img src="bg.jpg">
 <img src="bg2.jpg">
</body>
</html>

  

时间: 2024-10-12 17:15:04

深入理解Javascript封装DOMContentLoaded事件的相关文章

深入理解javascript中的事件循环event-loop

前面的话 本文将详细介绍javascript中的事件循环event-loop 线程 javascript是单线程的语言,也就是说,同一个时间只能做一件事.而这个单线程的特性,与它的用途有关,作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM.这决定了它只能是单线程,否则会带来很复杂的同步问题.比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准? 为了利用多核CPU的计算能力,HTML

理解Javascript中的事件绑定与事件委托

最近在深入实践js中,遇到了一些问题,比如我需要为动态创建的DOM元素绑定事件,那么普通的事件绑定就不行了,于是通过上网查资料了解到事件委托,因此想总结一下js中的事件绑定与事件委托. 事件绑定   最直接的事件绑定:HTML事件处理程序 如下示例代码,通过节点属性显式声明,直接在HTML中,显式地为按钮绑定了click事件,当该按钮有用户点击行为时,便会触发myClickFunc方法. /* html */ <button id="btn" onclick="myCl

理解JavaScript中的事件路由冒泡过程及委托代理机制

当我用纯CSS实现这个以后.我开始用JavaScript和样式类来完善功能. 然后,我有一些想法,我想使用Delegated Events (事件委托)但是我不想有任何依赖,插入任何库,包括jQuery.我需要自己实现事件委托了. 我们先来看看事件委托到底是什么?他们是怎么工作的,怎么去实现这种机制. 好,它解决了什么问题? 我们先看个简单的例子. 先假设我们有一组按钮,我一次点击一个按钮,然后我希望被点中的状态设为"active".再次点击时取消active. 然后,我们可以写一些H

理解javascript中的事件模型

javascript中有两种事件模型:DOM0,DOM2.而对于这两种的时间模型,我一直不是非常的清楚,现在通过网上查阅资料终于明白了一些. 一.  DOM0级事件模型 DOM0级事件模型是早期的事件模型,所有的浏览器都是支持的,而且其实现也是比较简单.代码如下: <p id = 'click'>click me</p> <script> document.getElementById('click').onclick = function(event){ alert(

理解JavaScript中的事件轮询

原文:http://my.oschina.net/u/154866/blog/211837 Event Loop是一个很重要的概念,指的是计算机系统的一种运行机制,JavaScript语言就采用的这种机制,来解决单线程运行带来的一些问题. 想要理解Event Loop,就要从程序的运行模式讲起.“进程”是指程序的一次执行,一般情况下,一个进程一次只能执行一个任务.线程是CPU的基本调度单位. 如果有多个任务需要执行,不外乎三种解决方法. 排队.因为一个进程一次只能执行一个任务,只好等前面的任务执

理解JavaScript中的事件流

原文地址:http://my.oschina.net/sevenhdu/blog/332014 目录[-] 事件冒泡 事件捕获 DOM事件流 当浏览器发展到第四代时(IE4和Netscape Communicator 4),浏览器团队遇到一个很有意思的问题:页面的哪一部分会拥有特定的事件?想象下在一张纸上有一组同心圆,如果你把手指放在圆心上,那么你的手指指向的不是一个圆,而是一组圆.两家公司的开发团队在看待浏览器事件方面还是一致的.如果你单击了某个按钮,那么同时你也单击了按钮的容器元素,甚至整个

深入理解javascript事件流

摘要:事件流这个东西是比较重要的,为了让自己更加理解js中的事件流,必须整理整理,梳理一下事件流的各种东西啊.本文大部分内容参考<javascript高级程序设计第三版> 先来一段书里的原文: 当浏览器发展到第四代时(IE4和Netscape Communicator 4),浏览器团队遇到一个很有意思的问题:页面的哪一部分会拥有特定的事件?想象下在一张纸上有一组同心圆,如果你把手指放在圆心上,那么你的手指指向的不是一个圆,而是一组圆.两家公司的开发团队在看待浏览器事件方面还是一致的.如果你单击

JavaScript异步编程(一) 深入理解JavaScript事件

JavaScript异步编程 深入理解JavaScript事件 ?事件的调度 JavaScript事件处理器在线程空闲之前不会运行 线程的阻塞 var start = new Date(); // setTimeout和setInterval的计时精度比期望值差 setTimeout(function(){ var end = new Date(); console.log('Time elapsed', end - start, 'ms'); }, 500); while(new Date -

深入理解JavaScript的闭包特性 如何给循环中的对象添加事件

初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件.在事件响应函数中(event handler)获取对应的索引.但每次获取的都是最后一次循环的索引.原因是初学者并未理解JavaScript的闭包特性. 有个网友问了个问题,如下的html,为什么点击所有的段落p输出都是5,而不是alert出对应的0,1,2,3,4. <!DOCTYPE HTML> <html> <head> <meta charset="utf-8" /> &