[转]jQuery DOM Ready
一直以来,各种JS最佳实践都会告诉我们,将JS放在HTML的最后,即</body>之前,理由就是:JS会阻塞下载,而且,在JS中很有可能有对DOM的操作,放在HTML的最后,可以尽可能的保证JS的执行在DOM加载完成之后。而如果放在onload事件中执行,如果页面有很多图像,那么页面的onload事件要过很久才会触发,因此DOM Ready事件就是最好的执行JS的时间了。
所以,如果有个DOM Ready事件就好了,虽然现代浏览器已经支持DOMContentLoaded事件,但是我们还是得处理那些老旧的浏览器,于是DOM Ready事件就成了各个JS框架的必备功能啦。
先来看看jQuery DOM Ready事件的用法吧,jQuery的DOM Ready事件用法很简单,大家都用过,下面的三种语法都是可用的:
$( document ).ready( handler )$().ready( handler )(不推荐)$( handler )
还有一种方式:$(document).on('ready', handler);,已经在1.8版本里被废弃了,但是你还可以这么用。这种方式和ready方法的作用一样,但是如果ready事件已经被触发过了,这种方式绑定的handler将不会被执行,而且用这种方式绑定的handler会在以上三种方法绑定的handler被执行后再执行。
一、DOM Ready检测
下面,我们按照jQuery的思路,一步步的看一下jQuery如何处理DOM Ready事件的。
1. 标准浏览器
对于标准浏览器,我们可以添加DOMContentLoaded事件监听器:
if ( document.addEventListener ) {
document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
}
2. 非标准浏览器
而对于非标准浏览器,我们可以监听document.onreadystatechange事件,如果document.readyState == ‘complete’时,可以认为DOM结构已经加载完成。
if ( document.addEventListener ) {
document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
// If IE event model is used
} else {
document.attachEvent( "onreadystatechange", DOMContentLoaded );
}
这里不用担心jQuery.ready()会被多次触发,因为在jQuery.ready()中有检测是否被触发过了。
4. 还是IE
在IE中,onreadystatechange事件对于iframe的来说可能会有延迟,但是却足够安全。但这个事件对于非iframe有时会不太可靠,特别是在页面中图片资源比较多的时候,可能反而在onload事件触发之后才能触发,因此对于这种情况,需要做进一步的检测:
if ( document.addEventListener ) {
document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
// A fallback to window.onload, that will always work
window.addEventListener( "load", jQuery.ready, false );
// If IE event model is used
} else {
document.attachEvent( "onreadystatechange", DOMContentLoaded );
// A fallback to window.onload, that will always work
window.attachEvent( "onload", jQuery.ready );
// If IE and not a frame
// continually check to see if the document is ready
var top = false;
try {
top = window.frameElement == null && document.documentElement;
} catch(e) {}
// 如果是非iframe的情况,并且支持doScroll方法
if ( top && top.doScroll ) {
// 在这个自执行函数中,调用top.doScroll方法,若报错,则延时50ms再检测;若不报错,则表示DOM Ready,可以执行jQuery.ready()方法了
(function doScrollCheck() {
if ( !jQuery.isReady ) {
try {
top.doScroll("left");
} catch(e) {
return setTimeout( doScrollCheck, 50 );
}
// and execute any waiting functions
jQuery.ready();
}
})();
}
}
是的,JS就是这么一门神(dan)奇(teng)的语言,每个浏览器的实现可能不尽相同,所以就出现了这种对一个事件的监听要做如此多的兼容考虑的情况。
5. Ready太晚了
还有一种情况是,当我们调用$(document).ready()的时候,DOM结构已经加载完成了,这时候可以直接调用jQuery.ready,但是因为IE的问题,需要延迟调用,所以有下面的代码,至于为什么要这么用,小Y也没看明白:
if ( document.readyState === "complete" ) {
// Handle it asynchronously to allow scripts the opportunity to delay ready
setTimeout( jQuery.ready, 1 );
} else if ( document.addEventListener ) {
document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
// A fallback to window.onload, that will always work
window.addEventListener( "load", jQuery.ready, false );
// If IE event model is used
} else {
document.attachEvent( "onreadystatechange", DOMContentLoaded );
// A fallback to window.onload, that will always work
window.attachEvent( "onload", jQuery.ready );
// If IE and not a frame
// continually check to see if the document is ready
var top = false;
try {
top = window.frameElement == null && document.documentElement;
} catch(e) {}
// 如果是非iframe的情况,并且支持doScroll方法
if ( top && top.doScroll ) {
// 在这个自执行函数中,调用top.doScroll方法,若报错,则延时50ms再检测;若不报错,则表示DOM Ready,可以执行jQuery.ready()方法了
(function doScrollCheck() {
if ( !jQuery.isReady ) {
try {
top.doScroll("left");
} catch(e) {
return setTimeout( doScrollCheck, 50 );
}
// and execute any waiting functions
jQuery.ready();
}
})();
}
}
到这里,jQuery对DOM Ready的检测就完成了,那么$(document).ready()是怎么工作的呢?
二、DOM Ready全过程
这里,我们按代码执行的顺序,来理解jQuery的DOM Ready的全部代码,看jQuery是如何一步步处理DOM Ready事件的。为了阅读的方便,小Y将代码做了删减,并对代码顺序做了调整,大家可以按从1到7的顺序看代码。这里不对Deferred对象做解释,因为小Y也还没有看,只是记住他的作用是维护一系列条件触发的回调函数。
jQuery.fn = jQuery.prototype = {
init: function( selector, context, rootjQuery ) {
if ( jQuery.isFunction( selector ) ) {
// 当调用$(callback); 时,会调用$(document).ready(callback); Ⅰ
return rootjQuery.ready( selector );
}
},
ready: function( fn ) {
// Add the callback
// 调用jQuery.ready.promise方法,返回一个Deferred对象 readyList,
// 然后将fn加入到readyList的成功的回调列表中。
// 如果readyList已存在,则直接返回readyList,然后直接调用这个回调函数fn
jQuery.ready.promise() Ⅱ
.done( fn ); Ⅳ
return this;
}
};
jQuery.ready.promise = function( obj ) {
if ( !readyList ) {
readyList = jQuery.Deferred();
// IE的bug
if ( document.readyState === "complete" ) {
setTimeout( jQuery.ready, 1 );
// 给标准浏览器绑定DOMContentLoaded事件以触发DOMContentLoaded方法
// 同时,绑定window.onload事件作为备用方案
} else if ( document.addEventListener ) {
document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
window.addEventListener( "load", jQuery.ready, false );
// 给IE浏览器绑定onreadystatechange事件,在DOMContentLoaded中判断
// readyState是否为complete
} else {
document.attachEvent( "onreadystatechange", DOMContentLoaded );
window.attachEvent( "onload", jQuery.ready );
// 对IE做进一步的兼容性检测
var top = false;
try {
top = window.frameElement == null && document.documentElement;
} catch(e) {}
if ( top && top.doScroll ) {
(function doScrollCheck() {
if ( !jQuery.isReady ) {
try {
// Use the trick by Diego Perini
// http://javascript.nwbox.com/IEContentLoaded/
top.doScroll("left");
} catch(e) {
return setTimeout( doScrollCheck, 50 );
}
// and execute any waiting functions
jQuery.ready();
}
})();
}
}
}
// 返回readyList
return readyList.promise( obj ); Ⅲ
};
// 接下来就是等待DOMContentLoaded/readyState==='complete'事件的发生了
// 移除DOMReady事件监听,执行jQuery.ready()
DOMContentLoaded = function() { Ⅴ
// 标准浏览器
if ( document.addEventListener ) {
document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
jQuery.ready();
// IE 浏览器
} else if ( document.readyState === "complete" ) {
document.detachEvent( "onreadystatechange", DOMContentLoaded );
jQuery.ready();
}
};
jQuery.extend({
// DOM Ready标志位,DOM Ready触发后置为true
isReady: false,
// DOM Ready后运行
ready: function( ) {
// 如果已经Ready,则直接返回
if ( jQuery.isReady ) {
return;
}
// 兼容IE
if ( !document.body ) {
return setTimeout( jQuery.ready, 1 );
}
// 将isReady置为true
jQuery.isReady = true; Ⅵ
// 这里是真正触发我们传入的回调的地方!!!!
// 传入上下文和回调函数的参数列表
// 注意啦,上下文是document,也就是说我们在`$(document).ready(fn);`里的fn中的this是`document`,不是`window`
// 参数列表传入了jQuery,意味着即使我们调用了`$.noConflict()`将$让渡出去了,我们仍然能在fn中定义第一个参数为$,代表jQuery
readyList.resolveWith( document, [ jQuery ] ); Ⅶ
// 这段代码是为了触发这种方式添加的DOM Ready回调: $(document).on('ready' , fn);
// 这种方式在1.8被废弃,但是没有被移除
// 你可以看到这种方式添加的DOM Ready回调是最后被执行的
if ( jQuery.fn.trigger ) {
jQuery( document ).trigger("ready").off("ready");
}
}
});
看完代码,让我们再来看一下jQuery的思路:
- Ⅰ. 调用rootjQuery的ready事件
- Ⅱ. 调用jQuery.ready.promose(),若readyList为空,将readyList赋值为一个Deferred对象;否则将回调函数添加到readyList成功回调列表中,然后跳转到第7步
- Ⅲ. 在jQuery.promise()中为DOM Ready事件添加监听器
- Ⅳ. 将回调函数fn添加到readyList的成功回调列表中
- Ⅴ. 当DOM Ready事件发生时,移除监听器,执行jQuery.ready()
- Ⅵ. 在jQuery.ready()中将isReady置为true
- Ⅶ. 执行readyList的成功回调列表中的方法
在jQuery.ready.promise()中,还涉及到了jQuery.Deferred和jQuery.Callback接口,这里暂不讨论,因为小Y也还没看,且听下回分析吧。
原文链接:http://www.html-js.com/article/Read-jQuery-jQuery-DOM-Ready
[转]jQuery DOM Ready的更多相关文章
- jquery dom ready, jqery2.1.1实现-源码分析
本文链接http://www.cnblogs.com/Bond/p/4178311.html jquery document ready的实现其很很简,虽说简单,其很很多人还是没去关注过它的实现.我 ...
- 【jQuery源码】DOM Ready
一直以来,各种JS最佳实践都会告诉我们,将JS放在HTML的最后,即</body>之前,理由就是:JS会阻塞下载,而且,在JS中很有可能有对DOM的操作,放在HTML的最后,可以尽可能的保 ...
- jquery的ready方法(DOM是否加载完)详解与使用
jquery的ready方法(准备DOM触发)还是比较复杂的,我们先看流程图:
- jQuery源码dom ready分析
一.前言 在平时开发web项目时,我们使用jquery框架时,可能经常这样来使用$(document).ready(fn),$(function(){}),这样使用的原因是在浏览器把DOM树渲染好之前 ...
- jQuery之ready源码分析
只要使用过jQuery的,想必对ready都不陌生,$(function(){})和$(document).ready(function(){})的使用更是习以为常. 要说到window.onload ...
- jQuery $(document).ready()和JavaScript onload事件
jQuery $(document).ready()和JavaScript onload事件 Why we need a right time? 对元素的操作和事件的绑定需要等待一个合适的时机,可以看 ...
- jquery的ready事件的实现机制浅析
页面初始化中,用的较多的就是$(document).ready(function(){//代码}); 或 $(window).load(function(){//代码}); 他们的区别就是,ready ...
- Web UI - Javascript之DOM Ready
最近终于稍微适应了工作环境,终于可以让自己缓口气.于是决定要写点东西,算是督促.记录和提升自己的学习.代码的世界,你不轮它,以后就会被它轮.这个系列尽量保持在一周或两周更一篇,目标是在创造内容的时候更 ...
- jQuery $(document).ready()和window.onload
jQuery $(document).ready()和window.onload 根据ready()方法的API说明http://api.jquery.com/ready/. 这个方法接收一个func ...
随机推荐
- 【Android开发笔记】生命周期研究
启动 onCreate onStart onResume 退出键 onPause onStop onDestroy 锁屏 & 按住 home键 & 被其他Activity覆盖(Sing ...
- jQuery-添加新元素的方法(append()、prepend()、before()、after())
1.以 HTML 创建新元素 var txt1="<p>Text.</p>"; 2.以 jQuery 创建新元素 var txt2=$("< ...
- python实现栈的算法
以下来源“悟空”的讲课视频,我只是对内容加深以下理解,做一些说明: 栈作为一种数据结构,是一种只能在一端进行插入和删除操作.它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要 ...
- selenium显示等待解决浏览器未加载完成查找控件的问题
问题描述:wap版支付成功后,跳转到支付成功页,查找的元素已出现,如图的:元素1,元素2,但是提示查找的元素超时,失败,并且每到这个页面都会报页面超时,不能查找到页面元素 原始代码: try{ op. ...
- Android(java)学习笔记79:Android中SimpleAdapter,ArrayAdapter和BaseAdapter常见的适配器
1. SimpleAdapter(BaseAdapter子类扩展类): simpleAdapter的扩展性最好,可以定义各种各样的布局出来,可以放上ImageView(图片)等.可以显示比较复杂的列表 ...
- bzoj1189 [HNOI2007]紧急疏散
Description 发生了火警,所有人员需要紧急疏散!假设每个房间是一个N M的矩形区域.每个格子如果是'.',那么表示这是一块空地:如果是'X',那么表示这是一面墙,如果是'D',那么表示这是一 ...
- javascript同步和异步的区别与实现方式
javascript语言是单线程机制.所谓单线程就是按次序执行,执行完一个任务再执行下一个. 对于浏览器来说,也就是无法在渲染页面的同时执行代码. 单线程机制的优点在于实现起来较为简单,运行环境相对简 ...
- 推荐一个WebIDE在线编程语言编译器C9.io
有时借用别人电脑或者不想在电脑上安装各种乱七八糟的IDE,就可以考虑 Web IDE.随着Web技术发展,很多语言的编译工作都可以利用Web 浏览器来完成. 1. 推荐国外的 C9.io 个人可以免费 ...
- AngularJS 应用
AngularJS模块(Module)定义了AngularJS的应用. AngularJS控制器(Controller)用于控制AngularJS应用. ng-app指令定义了应用,ng-contro ...
- 【解题报告】AtCoder ABC115 (附英文题目)
------------------------------迟到的AK---------------------------------- A - Christmas Eve Eve Eve Time ...