最近终于稍微适应了工作环境,终于可以让自己缓口气。于是决定要写点东西,算是督促、记录和提升自己的学习。代码的世界,你不轮它,以后就会被它轮。这个系列尽量保持在一周或两周更一篇,目标是在创造内容的时候更深刻的理解和提升自己所学的知识, 本质上,接触Javascript编程之美,我转到前端工作,其实就是因为非常喜欢和好奇Javascript(当然,也是享受频繁的用代码和用户交互的感觉)。

什么是DOM Ready

还记得刚学习Javascript的时候,老师就一直各种强调教导告诉你说js的代码一定要写到window.onload里面,如下代码:
 window.onload=function()
{
//registered in onload
document.getElementById('demo').innerHTML='hello world';
}

window.onload

  包括各种古老的书籍中都告诉你说为了保证js能够正确运行(因为js大部分都是频繁的操作DOM),应该把代码写在onload中,而window.onload表示着web页面中所有的元素都已经加载完毕,保证了所有的DOM元素都是可用的。然而,DOM元素是丰富多彩的, 例如:img。当然一个img请求一张图片,而这张图片很大、路径错误等原因会造成请求一直下不来,如果你的页面上有这样一张图片,那么window.onload将会一直等待这张图片的加载,直到图片请求已经下来了或确认图片请求异常。这是一个可怕的过程,如果一张 图片请求了10s,那么你的用户在电脑面前面对着浏览器那个正在加载的圆圈的时候,很有可能关掉你的页面。

  这是一个很简单的case:onload太慢了。小伙鸡如果你现在竟然还在用着window.onload那就真的图样图森破了。

DOMContentLoaded

  感觉到onload太慢了,firefox为DOM纳入了一个全新的事件,叫做DOMContentLoaded,这也就是我们后来所说的DOM Ready。DOM Ready和onload不同之处在于:onload等待页面所有元素加载完毕才会执行,而DOM Ready,则 是在DOM树构建完毕即执行。这一过程可以这么形容:onload的时候页面已经呈现,而DOM Ready完毕的时候甚至用户都还没有看见页面。我们可以更快的处理一些事情,而不是等到onload之后。 而这一事件,逐渐的被广大浏览器纳入,除了那些古老的IE浏览器。

现在的前端js框架类库,例如jQuery、Zepto、YUI、mass没有个DOM Ready都不好意思称自己为库、框架。刚开始学习jQuery的时候,就对这个DOM Ready非常的感兴趣,当时因为自己的境界还停留在学习jQuery的时候,所以只能远远观望。当驾驭了jQuery的时候,我们就应该透过现象看本质——一窥jQuery源码,探索里面的秘密。

  其实DOM Ready,实现上非常简单,就是注册个DOM Ready的事件,但是低版本的IE(IE678)要么没有DOMContentLoaded事件,要么就是DOMContentLoaded的实现上有bug。

  所以,我们自己动手来实现DOM Ready吧。

首先,w3c的实现,用w3c规范注册DOMContentLoaded事件,同时针对IE8 hack,一个简单的DOM Ready示例如下:
     //将一个函数注册到DOM Ready
function $(fn) {
if (document.addEventListener)//w3c browser
document.addEventListener("DOMContentLoaded", fn, false);
else if (document.attachEvent)//ie8+ browser
document.attachEvent("onreadystatechange", fn);
} //注册一个DOM Ready事件,是不是和jQuery很像?
$(function () {
console.log('hello world!');
});

DOMContentLoaded

我们不能忘记万恶的IE

  不要忘记,在你编写了一段在现代浏览器中运行起来各种没问题so beautiful,正觉得自己碉堡了的时候,你打开了IE,然后眼睁睁的看着它对你的精神各种侮辱xxoo的那种感觉吧。没错,万恶的IE

  上面的代码中,我们针对IE进行readystatechange进行绑定,IE为DOM的一部分提供readystatechange事件。然而,IE678,在页面中有iframe的情况下,DOM Ready的加载就会被影响,没错,它在等待iframe加载完毕才会执行readystatechange事件。

为此,我做了一番模拟,如下HTML:

 <!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>IE下readystatechange事件测试 - linkFly</title>
<script type="text/javascript"> //开始记录时间
var date = new Date();
+function () {
//注册readystatechange函数
document.attachEvent("onreadystatechange", function () {
//跟踪这个函数执行的时间
document.getElementById('demo').innerHTML = Math.floor((new Date()).getTime() - date.getTime()) / 1000;
});
}();
</script>
</head>
<body>
<div id="demo"></div>
<!--这里显示readystatechange时间差-->
<iframe src="https://github.com/" height="20" width="800"></iframe>
<div id="end"></div>
<!--这里是DOM Ready时间差-->
</body>
<script type="text/javascript">
//js写在这个位置,DOM可以说已经构建完毕,跟踪执行时间
document.getElementById('end').innerHTML = Math.floor((new Date()).getTime() - date.getTime()) / 1000;
</script>
</html>

readystatechange

  然后,就发生了一些有意思的事情,如下图:

  iframe里面特意链到Github,所以访问起来很慢(天朝,你懂的...)。可以看见,下面的DOM Ready的代码执行的很快,但是上面的readystatechange,ie8下我们忍了,但是这ie6、7是个神马情况???12s后才执行,简直就是坑爹夫斯基啊!!!!!这玩意儿尼玛不靠谱啊。

  我用写好的DOM Ready进行测试,效果如下:

  这速度,这效果,so beautiful啊...

  总结起来就是,有iframe的页面,readystatechange并不靠谱,那么我们需要单独处理一下这个情况,针对这一情况,最著名的莫过于Diego Perini发现的hack:

 +function(fn)//ie678检测iframe的DOMReady
{
try {
//IE下页面DOM没有加载完成,调用doScroll会报错
document.documentElement.doScroll("left");
//没有报错,证明DOM已经加载完毕,执行DOM Ready里需要执行事件
fn();
} catch (e) {
//重复调用自己
setTimeout(arguments.callee, 1);
}
}(function(){
document.getElementById('demo').innerHTML='hello world!';
});

ie678hack

  好的,现在我们也已经知道了怎么解决IE下的问题,那么现在,我们就应该把这两段代码合并,封装一个DOMReady库,下次使用我们直接就可以用了。没错,DOM Ready其实就这点东西,是不是so easy啊。那么我们准备封装类库吧。

  让我们想想,jQuery的DOM Ready是不是可以多个事件绑定到DOM Ready?那么我们也来实现这样的效果,检测DOM Ready,然后执行事件集合,在Js,就用数组来实现。

  其实代码上很大一部分参照了jQuery源码,但是jQuery解耦解的实在丧心病狂,所以一个DOM Ready跳转了不下30多个方法,当然,侦听DOM Ready的代码并不是很繁琐,jQuery主要在DOM Ready的将要执行的事件上进行了良好的解耦和封装。 好了话不多说,上源码:

源码

  Javascript源码:  

 /*!
* by - linkfly
* cnblogs - http://www.cnblogs.com/silin6/
*/
(function (window, undefined) {
/*
* checkReady
* event model:ready ->> bindReady ->>
* ie(iframe):doScrollCheck ->> checkReady
*/
var readyList = [], //DOM Ready执行的数组
document = window.document,
DOMContentLoaded, //DOMReady事件
isReady = false, //DOM是否准备完毕
triggerReady = function () { //触发ready事件
while (readyList.length) {
readyList[0].call(window); //指定上下文
readyList.shift();
}
};
//ready执行方法,检测需要的环境是否已经准备好,它是DOM Ready最后一道关卡
var checkReady = function () {
if (!document.body) { return setTimeout(checkReady, 1); }
isReady = true; //标识完成
triggerReady();
},
//本来还想使用一个wait数组表示当前正在等待执行的数据,但是因为下面用的addEventListener和attachEvent,所以直接让js Event维护即可
bindReady = function () { //绑定ready
//body必须存在
//如果DOM Ready,则直接触发,Firefox3.6之前并没有readyState,考虑市场因素抛弃这部分兼容
if (document.readyState === 'complete' || isReady) { return setTimeout(triggerReady, 1); }
if (document.addEventListener) {//w3c
document.addEventListener("DOMContentLoaded", DOMContentLoaded, false);
//如果没有赶上DOM Ready,则监听load
window.addEventListener("load", checkReady, false);
} else if (document.attachEvent) {//ie
document.attachEvent("onreadystatechange", DOMContentLoaded);
window.attachEvent("onload", checkReady);
//ie下多iframe
var toplevel = false;
try {
toplevel = window.frameElement == null;
} catch (e) { }
if (document.documentElement.doScroll && toplevel) {
doScrollCheck();
}
}
},
doScrollCheck = function () { //ie678检测iframe的DOMReady
try {
//IE下页面DOM没有加载完成,调用doScroll会报错
document.documentElement.doScroll("left");
checkReady();
} catch (e) {
setTimeout(doScrollCheck, 1);
}
},
ready = function (fn) { //DOM Ready
//判定fn有效性代码略过...
readyList.push(fn);
bindReady();
};
~function () {
if (document.addEventListener) {
DOMContentLoaded = function () {
document.removeEventListener("DOMContentLoaded", DOMContentLoaded, false);
checkReady();
};
} else if (document.attachEvent) {
DOMContentLoaded = function () {
if (document.readyState === "complete") {
document.detachEvent("onreadystatechange", DOMContentLoaded);
checkReady();
}
};
}
}();
window.$ = window.ready = ready;
})(window);

DOM Ready

  HTML源码:

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>测试DOM Ready - linkFly</title>
<!--<script src="jQuery/jquery-1.7.2.js" type="text/javascript"></script>-->
<script src="DOMReady.js" type="text/javascript"></script>
<script type="text/javascript">
//http://localhost:2969/test.html
$(function () {
document.getElementById('test').innerHTML += '<p>顶部:完毕</p>';
alert(window.frames[0].document.firstChild.innerHTML);
});
</script>
<style type="text/css">
img { height: 20px; width: 20px; }
</style>
</head>
<body>
<div id="test">
</div>
<div id="test2">
</div>
<div id="test3">
</div>
<img src="http://c.s-microsoft.com/en-us/CMSImages/MS_FavMoments_End_1600x540_EN_US.jpg?version=10582f1d-db23-3040-9cf3-3ec93ece226d&h=0"
alt="" />
<img src="http://c.s-microsoft.com/en-us/CMSImages/MS_FavMoments_End_1600x540_EN_US.jpg?version=10582f1d-db23-3040-9cf3-3ec93ece226d&h=1"
alt="" />
<img src="http://c.s-microsoft.com/en-us/CMSImages/MS_FavMoments_End_1600x540_EN_US.jpg?version=10582f1d-db23-3040-9cf3-3ec93ece226d&h=2"
alt="" />
<img src="http://c.s-microsoft.com/en-us/CMSImages/MS_FavMoments_End_1600x540_EN_US.jpg?version=10582f1d-db23-3040-9cf3-3ec93ece226d&h=3"
alt="" />
<img src="http://c.s-microsoft.com/en-us/CMSImages/MS_FavMoments_End_1600x540_EN_US.jpg?version=10582f1d-db23-3040-9cf3-3ec93ece226d&h=4"
alt="" />
<img src="http://c.s-microsoft.com/en-us/CMSImages/MS_FavMoments_End_1600x540_EN_US.jpg?version=10582f1d-db23-3040-9cf3-3ec93ece226d&h=5"
alt="" />
<img src="http://c.s-microsoft.com/en-us/CMSImages/MS_FavMoments_End_1600x540_EN_US.jpg?version=10582f1d-db23-3040-9cf3-3ec93ece226d&h=6"
alt="" />
<img src="http://c.s-microsoft.com/en-us/CMSImages/MS_FavMoments_End_1600x540_EN_US.jpg?version=10582f1d-db23-3040-9cf3-3ec93ece226d&h=7"
alt="" />
<img src="http://c.s-microsoft.com/en-us/CMSImages/MS_FavMoments_End_1600x540_EN_US.jpg?version=10582f1d-db23-3040-9cf3-3ec93ece226d&h=8"
alt="" />
<img src="http://c.s-microsoft.com/en-us/CMSImages/MS_FavMoments_End_1600x540_EN_US.jpg?version=10582f1d-db23-3040-9cf3-3ec93ece226d&h=9"
alt="" />
<iframe src="https://github.com/" height="200" width="800"></iframe>
<!--测试iframe跨域-->
</body>
<script type="text/javascript">
$(function () {
document.getElementById('test2').innerHTML += '<p>底部:完毕</p>';
});
</script>
</html>

HTML Demo

  到了这里,是不是有点觉得onload就应该被舍弃呢?其实不然,onload也不是一无是处,例如chrome不同的图片渲染引擎,造成了无法在DOM中处理图片,当然这也是合理的,DOM树中或许图片都还没有下载呢,遇见过这样的情况,只能在onload中对图片进行处理。  

源码下载地址

 
作者:linkFly
声明:嘿!你都拷走上面那么一大段了,我觉得你应该也不介意顺便拷走这一小段,希望你能够在每一次的引用中都保留这一段声明,尊重作者的辛勤劳动成果,本文与博客园共享。

Web UI - Javascript之DOM Ready的更多相关文章

  1. web前端----JavaScript的DOM(二)

    前面在DOM一中我们知道了属性操作,下面我们来了解一下节点操作.很重要!! 一.节点操作 创建节点:var ele_a = document.createElement('a');添加节点:ele_p ...

  2. 松软科技web教程:JavaScript HTML DOM 元素

    查找 HTML 元素 通常,通过 JavaScript,您需要操作 HTML 元素. 为了达成此目的,您需要首先找到这些元素.有好几种完成此任务的方法: 通过 id 查找 HTML 元素 通过标签名查 ...

  3. web前端----JavaScript的DOM(三)

    一.JS中for循环遍历测试 for循环遍历有两种 第一种:是有条件的那种,例如    for(var i = 0;i<ele.length;i++){} 第二种:for (var i in l ...

  4. web前端----JavaScript的DOM(一)

    一.什么是HTML  DOM HTML  Document Object Model(文档对象模型) HTML DOM 定义了访问和操作HTML文档的标准方法 HTML DOM 把 HTML 文档呈现 ...

  5. 松软科技Web课堂:JavaScript HTML DOM 动画

    基础页面 为了演示如何通过 JavaScript 来创建 HTML 动画,我们将使用一张简单的网页: 实例 <!DOCTYPE html> <html> <body> ...

  6. 【JavaScript】谈谈Google Polymer以及Web UI框架的未来

    摘要:开发者Axel Rauschmayer在自己的博客上详解了Google Polymer的设计理念与组成架构,深得Polymer开发者的认同.他认为Polymer这样高互操作性的设计才应该是Web ...

  7. 10个优秀的JavaScript Web UI库/框架推荐

    在进行Web开发时,并非所有的库都适合你的项目,但你仍需要收藏一些Web UI设计相关的库或框架,以在你需要的时候,加快你的开发效率. 本文为你带来10款非常优秀的基于JavaScript的Web U ...

  8. 第十五章:Python の Web开发基础 (二) JavaScript与DOM

    本課主題 JavaScript 介绍 DOM 介绍 JavaScript 介绍 JavaScript 是一门编程语言,它可以让网页动起来的,JavaScript 的变量有两种,一个是局部变量:一个是全 ...

  9. 推荐10款优秀的JavaScript Web UI库 框架和套件

    在进行Web开发时,并非所有的库都适合你的项目,但真正开发的时候,你任然需要依赖一款UI框架.特别在你时间紧迫的时候,它是你忠实的朋友. 他们都是些广泛使用包含不同语言实现的WEB UI框架.今天我就 ...

随机推荐

  1. 动态单链表的传统存储方式和10种常见操作-C语言实现

    顺序线性表的优点:方便存取(随机的),特点是物理位置和逻辑为主都是连续的(相邻).但是也有不足,比如:前面的插入和删除算法,需要移动大量元素,浪费时间,那么链式线性表 (简称链表) 就能解决这个问题. ...

  2. Deep learning:五十(Deconvolution Network简单理解)

    深度网络结构是由多个单层网络叠加而成的,而常见的单层网络按照编码解码情况可以分为下面3类: 既有encoder部分也有decoder部分:比如常见的RBM系列(由RBM可构成的DBM, DBN等),a ...

  3. CSS Vocabulary – CSS 词汇表,你都掌握了吗?

    CSS 是前端开发必备技能,入门容易,深入难.比如像 Pseudo-class.Pseudo-element.Media query.Media type 以及 Vendor prefix 的概念,很 ...

  4. Myth – 支持变量和数学函数的 CSS 预处理器

    Myth 是一个预处理器,有点类似于 CSS polyfill .Myth 让你写纯粹的 CSS,同时还让你可以使用类似 LESS 和 Sass 的工具.您仍然可以使用变量和数学函数,就像你在其它预处 ...

  5. 白话Https

    本文试图以通俗易通的方式介绍Https的工作原理,不纠结具体的术语,不考证严格的流程.我相信弄懂了原理之后,到了具体操作和实现的时候,方向就不会错,然后条条大路通罗马.阅读文本需要提前大致了解对称加密 ...

  6. Windows Azure Virtual Network (7) 设置Azure Virtual Machine固定公网IP (Virtual IP Address, VIP) (2)

    <Windows Azure Platform 系列文章目录> 本文介绍的是,当用户在创建Azure Virtual Machine的时候,忘记绑定公网IP,需要重新绑定公网IP的具体操作 ...

  7. Elasticsearch——multi termvectors的用法

    前一篇已经翻译过termvectors的使用方法了,这对于学习如何使用tf-idf来说是很有帮助的了. 更多内容参考我整理的ELK教程 什么是TF-IDF? 今天早晨起来,看<ES IN ACT ...

  8. DirectShow .Net 实现视频

    DirectShow .Net 实现视频 .获取视频采集设备IBaseFilter接口对象的方法 //获取所有视频设备名称 public ArrayList GetVideoInputDevice() ...

  9. SQL中 将同一个表中的A列更新到B列,B列更新到A列

    有网友在SKYPE问及,如标题,SQL中 将同一个表中的A列更新到B列,B列更新到A列. 其实这个不是问题,直接写更新语句即可,可以参考下面动画演示: SQL source code: CREATE ...

  10. SQL Pretty Printer-不错的SQL格式化工具

    前言 好长时间没有写过博客了,人变懒了很多,应该说本来也不怎么勤快.但今天为了这个工具,必须得勤快一下了,天下真的没有免费的午餐. 之前使用过sql server 2000的查询设计器和Toad fo ...