domready,DOM Ready 详解

插播广告: 百度商务搜索部, 招聘前端测试工程师! 待遇优厚! 要求JS, Linux基础. 有意向的同学请直接联系我!

DOM Ready 概述    

熟悉jQuery的人, 都知道DomReady事件. window.onload事件是在页面所有的资源都加载完毕后触发的. 如果页面上有大图片等资源响应缓慢, 会导致window.onload事件迟迟无法触发.所以出现了DOM Ready事件. 此事件在DOM文档结构准备完毕后触发, 即在资源加载前触发. 另外我们需要在DOM准备完毕后, 再修改DOM结构, 比如添加DOM元素等. 否则有可能出现“Internet Explorer无法打开站点”的问题. 要模拟此错误, 可以在页面上添加下面的代码, 并用IE6打开:


    有关DOM Ready事件的实现,包括jQuery中的DomReady实现, 在国内和国外网站上已经早有人分享了经验, 并提出了许多方法.
为了避免人云亦云, 抱着怀疑的态度, 我去研究了这些DOM Ready方法. 只会使用Google搜索或者jQuery等类库, 不会帮助前端开发人员进步.所以弄懂其中的原理才是关键.
对于FF, Chrome, Safari, IE9等浏览器:
DOMContentLoaded 事件在许多Webkit浏览器以及IE9上都可以使用, 此事件会在DOM文档准备好以后触发, 包含在HTML5标准中. 对于支持此事件的浏览器, 直接使用DOMContentLoaded事件是最简单最好的选择.
对于IE6,7,8:
不幸的是, IE6,7,8都不支持DOMContentLoaded事件.所以目前所有的hack方法都是为了让IE6,7,8支持DOM Ready事件.
鉴于下面的统计结果(2011年5月统计的数据), 我们必须支持IE6,7,8, 一个都不能少!
中国某音乐站:
  • doScroll: doScroll通过时readyState可能为interactive, 也可能为complete. 但是一定会在DOM结构稳定后, 图片加载完毕前执行.
所以可以看出, 目前的setTimeout方法, 外部script和内部script方法, 都是存在错误的.应该说这些方法不能安全可靠的实现DomReady事件.
而单纯使用readyState属性是无法判断出Dom Ready事件的. interactive状态过早(DOM没有稳定), complete状态过晚(图片加载完毕).
jQuery实现中使用的doScroll方法是目前唯一可用的方法.
在本文的最后, 提供了使用本原理实现的ready函数. 其实和jQuery中的Dom Ready原理几乎一样. 但是其中加入了延时, 可以指定win对象(即支持iframe)等功能.

360+IE6:

 
add DOM _disibledevent=>internal script defer, readyState:interactive
[延时2秒加载js脚本]
add DOM in delay script, readyState:interactive
jQuery ready, readyState:interactive
doScroll, readyState:interactive
[延时8秒加载图片]
onreadystatechange, readyState:complete
window.onload, readyState:complete
setTimeout , readyState:complete
添加了defer的的外部js都没有执行.
对360这种没有技术, 没有任何优点, 单纯的在IE上面加个外壳, 添加自己的产品和广告的浏览器, 应该予以鄙视! 虽然凭借"安全卫士推荐用浏览器"占有了很多的国内市场, 从运维和产品层面看是成功的, 但是却是不道德的. 所谓的"安全"只是一个幌子而已! 真正了解互联网的用户不应该使用360浏览器. 360安全卫士是一款不错的产品, 最初的定位很好. 只是目前越做越大, 手伸的越来越深. 挟用户威胁厂商的盈利模式.不许别人出广告, 只能自己产品出广告.当一款好的产品在一个没有道德底线的人手里时, 用户变得很难取舍.

IE6:

 
add DOM _disibledevent=>[external script defer(1), readyState:interactive--经常无此项]
internal script defer, readyState:interactive
[延时2秒加载js脚本]
add DOM in delay script, readyState:interactive
external script defer (2), readyState:interactive
jQuery ready, readyState:interactive
doScroll, readyState:interactive
[延时8秒加载图片]
onreadystatechange, readyState:complete
window.onload, readyState:complete
setTimeout , readyState:complete
第一个添加了defer的外部js, 时有时无. 大部分时间没有.

IE7:

 
add DOM _disibledevent=>[external script defer(1), readyState:interactive--经常无此项]
internal script defer, readyState:interactive
add DOM in delay script, readyState:interactive
external script defer (2), readyState:interactive
onreadystatechange, readyState:complete
jQuery ready, readyState:complete
window.onload, readyState:complete
setTimeout , readyState:complete
第一个添加了defer的外部js, 时有时无. 大部分时间没有.

IE8:

 
add DOM _disibledevent=>external script defer (1), readyState:interactive
internal script defer, readyState:interactive
external script defer (2), readyState:interactive
add DOM in delay script, readyState:interactive
onreadystatechange, readyState:complete
jQuery ready, readyState:complete
window.onload, readyState:complete
setTimeout , readyState:complete
IE9:
 
add DOM _disibledevent=>external script defer (1), readyState:interactive
internal script defer, readyState:interactive
external script defer (2), readyState:interactive
add DOM in delay script, readyState:interactive
jQuery ready, readyState:interactive
onreadystatechange, readyState:complete
window.onload, readyState:complete
setTimeout , readyState:complete
 

DOM Ready实现代码

下面是我的Dom Ready的实现代码. 与jQuery的相比, 所有以来的函数都在ready的定义中, 易于理解和维护.  并且自己实现更加具有灵活性, 加入了时间延时已经传递window对象的能力.
/**
Dom Ready Event
*/
ready : function( callback , delay, win){
win = win || this.win || window;
var doc = win.document;
delay = delay || 0;
this.domReadyMonitorRunTimes = 0;
//将时间函数放入数组, 在DomReady时一起执行.
this.readyFuncArray = this.readyFuncArray || [];
this.readyFuncArray.push({func:callback, delay:delay, done:false});
//domReadyMonitor为监控进程的事件处理函数
var domReadyMonitor = (function(){
var isReady = false;
this.domReadyMonitorRunTimes++;
//对于非iframe嵌套的ie6,7,8浏览器, 使用doScroll判断Dom Ready.
if(this.browser.ie && this.browser.ie<9 && !win.frameElement){
try {
doc.documentElement.doScroll("left");
isReady = true;
}
catch(e) {
}
}
//非ie浏览器
//如果window.onload和DOMContentLoaded事件都绑定失败, 则使用定时器函数判断readyState.
else if(doc.readyState==="complete" || this.domContentLoaded ){
isReady = true;
}
//对于某些特殊页面, 如果readyState永远不能为complete, 设置了一个最大运行时间5分钟. 超过了最大运行时间则销毁定时器.
//定时器销毁不影响window.onload和DOMContentLoaded事件的触发.
else{
if(this.domReadyMonitorRunTimes > 300000){
if(this.domReadyMonitorId){
win.clearInterval(this.domReadyMonitorId);
this.domReadyMonitorId = null;
}
return;
}
}

//执行ready集合中的所有函数
if(isReady){
try{
if(this.readyFuncArray && this.readyFuncArray.length){
for(var i=0, count=this.readyFuncArray.length; i var item = this.readyFuncArray[i];
if(!item || !item.func || item.done){
continue;
}
if(!item.delay){
item.done = true;
item.func();
}
else{
item.done = true;
win.setTimeout(item.func, item.delay);
}
}
}
}
catch(ex){
throw ex;
}
finally{
if(this.domReadyMonitorId){
win.clearInterval(this.domReadyMonitorId);
this.domReadyMonitorId = null;
}
}
}
}).__proxy(this);
/**
domContentLoadedHandler直接执行所有ready函数.
没使用传参的形式是因为ff中的定时器函数会传递一个时间参数.
*/
var domContentLoadedHandler = (function(){
this.domContentLoaded = true;
domReadyMonitor();
}).__proxy(this);
//启动DomReady监控进程
if(!this.domReadyMonitorStarted){
this.domReadyMonitorStarted = true;
this.domReadyMonitorId = win.setInterval( domReadyMonitor, 50);
// Mozilla, Opera and webkit nightlies currently support this event
if ( doc.addEventListener ) {
// Use the handy event callback
doc.addEventListener( "DOMContentLoaded", domContentLoadedHandler, false );
// A fallback to window.onload, that will always work
win.addEventListener( "load", domContentLoadedHandler, false );
}
else if(doc.attachEvent){
// A fallback to window.onload, that will always work
win.attachEvent( "onload", domContentLoadedHandler, false );
}
}
}
 
使用举例:
1: 
2: //正常使用
3: this.U.ready( this.windowOnLoadHandler );
4: //延时2秒加载
5: this.U.ready(this.windowOnLoadDelayHandler, 2000);

 

结尾

如果大家发现上面的实现有Bug, 请在 回复中反馈. 谢谢大家!
因为近期忙碌, 一直没有来得及推广我的"jQuery风暴"一书. 看到上一次出版前的推广帖子, 有人反馈了一些问题, 个人凭良心说, 书说不上技术讨论的有多深, 但是一定是阅读和学习起来最容易的. 里面的错别字问题的确蛋疼, 各位就多多包涵吧 T_T
Tags: 

延伸阅读

最新评论

发表评论