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);
转载:张子秋 http://www.cnblogs.com/zhangziqiu/
转载于:https://www.cnblogs.com/-simon/p/5889722.html
DOM Ready 详解相关推荐
- JQUREY DOM 操作详解
JQUREY DOM 操作详解 一.获取 1.获取内容----.text() .html() .value() text() - 设置或返回所选元素的文本内容 ...
- php 赋值给 dom对象,详解PHP原生DOM对象操作XML的方法
详解PHP原生DOM对象操作XML的方法 发布于 2017-08-08 20:15:29 | 80 次阅读 | 评论: 0 | 来源: 网友投递 PHP开源脚本语言PHP(外文名: Hypertext ...
- Jquery中的$(document).ready()详解
参考:https://blog.csdn.net/beidaol/article/details/81009740 因为html代码顺序执行,所以想要在js操作某个标签元素的时候,要保证这个元素已经被 ...
- dom jaxp详解
转自 http://blog.csdn.net/java958199586/article/details/7277904 一.XML解析技术概述 1.XML解析方式分为两种:dom和sax (1)d ...
- 你不知道的JavaScript--Item29 DOM基础详解
看完JavaScript高级程序设计,整理了一下里面的DOM这一块的知识点,比较多,比较碎!DOM在整个页面的地位如图: DOM(文档对象模型)是针对HTML 和XML 文档的一个API(应用程序编程 ...
- JavaScript入门3JS外置对象:Window、Document对象与DOM实例详解
一)window对象 1.什么是DHTML? 1)DHTML是指操作HTML以创造各种动态视觉效果,是一种浏览器端的动态网页技术. 2)DHTML的功能: ①动态改 ...
- php遍历dom节点,详解PHP使用DOMDocument类遍历、增加、修改、删除XML节点操作
Dom ( Document object model )文档 - 对象 - 模型,核心思想是把一个 Xml 文件看成一个对象模型,然后通过对象的方式来操作 Xml 文件.我们先总结出 DOM 对象的 ...
- XML解析之DOM解析详解
一.概念 xml文件多用于信息的描述,所以在得到一个xml文档之后按照xml中的元素取出对应的信息就是xml的解析.Xml解析有两种方式,一种是DOM解析,另一种是SAX解析,两种操作的方式如图. 二 ...
- 利用jquery操作ajax,利用jquery对ajax操作,详解原理(附代码)
1.jQuery load() 方法 jQuery load() 方法是简单但强大的 AJAX 方法. load() 方法从服务器加载数据,并把返回的数据放入被选元素中. 语法: $(selector ...
最新文章
- Django快速开发之投票系统
- 百度编辑器修改,不让它自动替换html标签
- jenkins 漏洞集合 简介
- 爱泼斯坦事件发酵,MIT师生发起抗议逼迫校长Rafael Reif辞职
- python中的互斥锁
- Flink的并行度和Kafka的partition的结合
- shadow阴影属性
- 机器学习:分类算法SVM(支持向量机)
- Python入门学习资料分享
- C# 阿里云视频点播
- 采集新闻数据的10个经典方法
- canvas制作圆角矩形(包括填充矩形的功能)
- Win10截图和草图无法使用怎么办
- VS2019c++配置GDAL和HDF库新手入门
- 微擎 人人商城 对接京东vop 对接京东商品,同步商品 地址,库存,价格,上下架等。(二)上 设置后台管理界面...
- 关注中国IT产业的明天 (转)
- c语言程序设计科研训练报告,科研训练总结精选 .doc
- 小程序毕业设计 基于微信电影院购票小程序毕业设计开题报告功能参考
- ubuntu net-tools
- 用通俗的解释,向你解释清楚大数据、人工智能和机器人之间的关系
热门文章
- 鸿蒙OS是什么架构,简单易懂:什么是华为「鸿蒙 OS」 - 无主界
- 羊毛党、低价值渠道统统闪开!友盟+反作弊能力来喽~
- cmakelist 库依赖库_将第三方库添加到CMakeList
- NW小世界网络模型python代码实现及平均路径聚类系数计算
- H3C AC+AP三层组网架构,AP自动上线自动固化
- 这本对我影响最大的书,分享给你!
- ElasticSearch入门简介、安装ES(安装Kibana和IK分词器)使用 Postman连接ES进行测、ESRestAPI(操作索引CRUD操作文档CRUD)、练习
- VoIP网络电话,VoIP渐成摇钱树 市场容量已达20亿美元
- 《微课实战:Camtasia Studio入门精要》——2.5 案例
- 转移到ios下载安卓_转移到ios下载官方app-转移到ios手机版v3.0.1 安卓版 - 极光下载站...