转自:http://www.cnblogs.com/zhangziqiu/archive/2011/06/27/DOMReady.html
DOM Ready 详解
DOM Ready 概述
熟 悉jQuery的人, 都知道DomReady事件. window.onload事件是在页面所有的资源都加载完毕后触发的. 如果页面上有大图片等资源响应缓慢, 会导致window.onload事件迟迟无法触发.所以出现了DOM Ready事件. 此事件在DOM文档结构准备完毕后触发, 即在资源加载前触发. 另外我们需要在DOM准备完毕后, 再修改DOM结构, 比如添加DOM元素等. 否则有可能出现“Internet Explorer无法打开站点”的问题. 要模拟此错误, 可以在页面上添加下面的代码, 并用IE6打开:
<div>
<script type="text/javascript"
>
var div = document.createElement('div' );
div.innerHTML = "test"
;
document.body.appendChild(div);
</script>
</div>
有关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, 一个都不能少!
中国某音乐站:
CNZZ:
另外鉴于"360浏览器"的占有率, 还要支持"360+IE6"这种无敌组合.
DOM Ready 实现方法
首先总结一下目前IE下的DOM Ready方法:
(1)setTimeout : 在setTimeout中触发的函数, 一定会在DOM准备完毕后触发.
示例代码:
1: //setTimeout Dom Ready
2:
var setTimeoutReady = function(){
3: document.getElementById("divMsg" ).innerHTML += "<br/> setTimeout , readyState:" + document.readyState;
4:
};
5:
var setTimeoutBindReady = function(){
6: /in /.test(document.readyState)?setTimeout(arguments.callee, 1):setTimeoutReady();
7:
};
8:
setTimeoutBindReady();
(2)readyState: 判断readyState的状态是否为Complete, interactive等触发
示例代码:
1: //onreadystatechange event
2:
document.onreadystatechange = function(e){
3: document.getElementById("divMsg" ).innerHTML += "<br/> onreadystatechange, readyState:" + document.readyState;
4:
5:
};
(3)外部script: 通过设置了script块的defer属性实现.
参见: http://dean.edwards.name/weblog/2005/09/busted/
示例代码:
1: <script type="text/javascript" src="ext-1.js" defer></script>
(4)内部script: 外部script的改进版本. 外部script需要页面引用额外的js文件. 内部script方法可以避免此问题.
参见: http://dean.edwards.name/weblog/2006/06/again/
示例代码:
1: //script defer Dom Ready
2: document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>" );
3: var script = document.getElementById("__ie_onload" );
4:
script.onreadystatechange = function() {
5: if (this .readyState == "complete" ) {
6: document.getElementById("divMsg" ).innerHTML += "<br/>internal script defer, readyState:" + document.readyState;
7:
}
8:
};
(5)doScroll : 微软的文档只出doScroll必须在DOM主文档准备完毕时才可以正常触发. 所以通过doScroll判断DOM是否准备完毕.
参见: http://javascript.nwbox.com/IEContentLoaded/
示例代码:
//doScroll
var doScrollMoniterId = null ;
var doScrollMoniter = function (){
try
{
document.documentElement.doScroll("left"
);
document.getElementById("divMsg" ).innerHTML += "<br/>doScroll, readyState:" + document.readyState;
if
(doScrollMoniterId){
clearInterval(doScrollMoniterId);
}
}
catch
(ex){
}
}
doScrollMoniterId = setInterval(doScrollMoniter, 1);
DOM Ready 调研结论
测试结果见下面. 在这里先给出结论.
- setTimeout设置的函数, 会在readyState为complete时触发, 但是触发时间点是在图片资源加载完毕后.
- readyState为interactive时, DOM结构并没有稳定, 此时依然会有脚本修改DOM元素.
- readyState为complete时, 图片已经加载完毕, 实验中对图片加载设置了延时.所以complete虽然在window.onload前执行, 但是还是太晚.
- 外部script:如果将此script放在页面上方, 则无法稳定触发. 并且触发时DOM结构依然可能发生变化.
- 内部script:与外部script同样的问题, 触发的时间过早.
- 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 onload, readyState:interactive
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 onload, readyState:interactive
[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 onload, readyState:interactive
[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 onload, readyState:interactive
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 onload, readyState:interactive
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的相比, 除了"__proxy函数"(在后面会讲解), 其他的依赖函数都在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<count; 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 );
}
}
}
上面的ready函数, 使用了一个Function对象的__proxy方法. 这是因为ready函数定义在一个使用JSON格式创建的对象中:
var
MyClass = {
ready : function
(){}
}
“__proxy”函数用于修改实现函数的this上下文, 与jQuery中的proxy函数类似, 但是为了使用更加优雅的语法, 所以注入到了Function原型中. 如果不需要修改this可以自行ready函数中对"___proxy"函数的依赖.
"__proxy"函数代码如下:
//扩展function函数原型。为所有的function对象添加proxy函数,用于修改函数的上下文。
Function.prototype.__proxy = function
(context){
var method = this ;
return function () {
return
method.apply(context || {}, arguments);
};
};
使用举例:
1:
2: //正常使用
3: this .U.ready( this .windowOnLoadHandler );
4: //延时2秒加载
5: this .U.ready(this .windowOnLoadDelayHandler, 2000);
结尾
如果大家发现上面的实现有Bug, 请在 回复中反馈. 谢谢大家!
因为近期忙碌, 一直没有来得及推广我的"jQuery风暴"一书. 看到上一次出版前的推广帖子, 有人反馈了一些问题, 个人凭良心说, 书说不上技术讨论的有多深, 但是一定是阅读和学习起来最容易的. 里面的错别字问题的确蛋疼, 各位就多多包涵吧 T_T
发表评论
-
[转:]jQuery焦点中国地图
2013-04-17 11:05 1684转自:http://xiaoyaosr.blog.51cto ... -
一个轻量级的网页遮罩层jQuery插件
2012-11-14 17:48 6368一个轻量级的网页遮罩层jQuery插件 分 ... -
通过Spring Mail Api发送邮件
2012-11-14 17:47 1440通过Spring Mail Api发送邮件 ... -
jquery.validate
2012-11-09 16:51 1070jquery.validate 需要JQuery ... -
Hibernate commit() 和flush() 的区别(转)
2012-09-28 09:46 2632Hibernate commit() 和flush( ... -
maven3.0.2下settings.xml配置下(转)
2012-09-25 19:14 5940maven3.0.2下settings.xml配置下 ... -
hibernate中Qurey类的setDate方法自动截掉时分秒——setDate和setTimeStamp(转)
2012-09-03 10:30 3077原帖地址:http://www.iteye.com/to ... -
Maven3.0.3的环境变量配置
2012-07-19 11:22 3031转自http://hi.baidu.com/douxinchu ... -
转:org.apache.catalina.core.JreMemoryLeakPreventionListener解决办法
2012-07-18 15:38 5524转自:http://blog.csdn.net/ang ... -
eclipse中的java文件图标j成空心
2012-07-06 10:06 13552eclipse中的java文件图标j成空心 ... -
jQuery - Ajax - readyState获取请求操作的当前状态
2012-06-29 14:46 0jQuery - Ajax - readyState获取请求 ... -
Dom加载判断,兼容所有浏览器
2012-06-29 14:35 9210Dom加载判断,兼容所有浏览器 Dom 加载完毕,兼容 ... -
document.readyState几种状态及示例
2012-06-29 14:33 2000document.readySta ... -
转】js实现另存为的方法
2012-06-29 13:42 2914转】js实现另存为的方法 本文转自http://www ... -
js添加和去除千分号
2012-06-22 11:41 2125利用js的replace方法和正则表达式实现 1 ... -
Hibernate的游离态与持久态转换(转)
2012-06-18 10:30 5224本文转自:http://blog.csdn.net/sdban ... -
maven继承parent,relativePath warn信息的解决办法
2012-06-11 17:12 26007转自:http://blog.sina.com.cn/s/bl ... -
使用Maven管理Eclipse Java项目
2012-06-11 17:10 1068转自:http://www.blogjava.net/lost ... -
The parent project must have a packaging type of POM
2012-06-11 17:09 25256The parent project must ha ... -
(转)利用eclipse构建和部署maven工程
2012-05-31 12:44 2350本文转自http://ll-feng.iteye.c ...
相关推荐
jQuery库的其中一个核心功能是文档就绪事件,通常称为ready事件,它的作用是在DOM完全加载并解析完成之后执行一些代码。本文详细介绍了jQuery ready方法的实现原理和使用方法。 ready方法的作用是确保在页面的HTML...
在jQuery中,遍历DOM(文档对象模型)是开发者经常使用的功能,用于高效地操作页面上的元素。本文将深入解析jQuery中的几个关键遍历方法,包括`parent()`、`parents()`、`parentsUntil()`、`find()`、`eq()`和`...
今天闲来无事研究研究jQuery...所以出现了DOM Ready事件. 此事件在DOM文档结构准备完毕后触发, 即在资源加载前触发. jQuery中的ready方法实现了当页面加载完成后才执行的效果,但他并不是[removed]或者doucment.onloa
### jQuery事件的ready()方法详解 #### 什么是ready()方法? jQuery的ready()方法是一个非常重要的函数,用于确保在DOM完全加载并且可以安全操作之后执行代码。它的主要作用是在页面的HTML被完全加载和解析之后,...
### jQuery实现ready和bind事件知识点详解 #### Jquery ready事件 在JavaScript中,页面加载完成后执行一段代码是一个非常常见的需求。传统上,我们会使用`window.onload`事件来实现这一点。但`window.onload`有其...
与load事件不同,ready事件在DOM完全就绪后立即触发,此时页面的所有DOM元素都已经加载完毕,但是一些外部资源(如图片、样式表等)可能还没有完全加载完成。ready事件可以附加到window对象、document对象或任何DOM...
$(document).ready()是DOM结构绘制完毕后就执行,不必等到加载完毕。 2.编写个数不同[removed]不能同时编写多个,如果有多个[removed]方法,只会执行一个$(document).ready()可以同时编写多个,并且都可以得到执行 3...
$(document).ready(function(){ $("button").click(function(){ $("p").hide(); $("#warning").fadeIn("slow"); }); }); ``` 在此示例中,当按钮被点击时,会隐藏所有`<p>`标签,并让带有ID“warning”的元素...
1. **页面加载事件**:`$(document).ready(function() {...})`确保DOM加载完成后执行代码。 2. **动态内容更新**:通过Ajax获取数据并更新页面部分区域,提高用户体验。 3. **交互式表单验证**:利用jQuery检查...
3. **文档就绪事件**:`$(document).ready(function() { ... })` 可以替代 `body` 标签的 `onload` 事件,确保在 DOM 加载完成后执行代码。这种方式更加灵活,可以在多个脚本中使用而不引起冲突。 4. **事件处理**...
《DOMWindow:基于jQuery的弹出层插件详解与实例应用》 在Web开发中,弹出层(Popup Layer)是一种常见的用户交互元素,用于显示警告、确认对话框或者提供更多的信息空间。DOMWindow是一款基于jQuery的弹出层插件,...
使用jQuery的好处是它包装了各种浏览器版本对DOM对象(javascript的DOM对象你应该知道吧,就是它了)的操作. 比如jquery写法:$(“div p”); // (1)$(“div.container”); // (2)$(“div #msg”); // (3)$(“table a”...
其次,$(document).ready()函数所做的事情仅限于DOM元素,而不涉及其他如图片、样式表等资源的加载情况。因此,它的触发时机比onload更早。 通过使用$(document).ready()函数,开发者可以确保其JavaScript代码只在...
**jQuery与DOM交互详解** jQuery,一个轻量级的JavaScript库,因其简洁的API和强大的功能,成为Web开发中广泛使用的工具。它极大地简化了JavaScript对DOM(Document Object Model)的操作,使得网页动态化和事件...
### jQuery详解:从入门到精通 #### 一、引言:jQuery是什么? jQuery是一个轻量级的JavaScript库,它的设计宗旨是“Write Less, Do More”(少写多做),极大地简化了HTML文档遍历、事件处理、动画以及Ajax交互等...