首先阅读Deferred相关知识:
测试1;
$(document).ready(function(){
alert("ready");
});
//这种方式调用以后,还要通过triggerHander来触发文档的ready事件!所以这个例子会执行两次ready!
//触发以后就要移除这个事件!因为ready事件不要多次调用!
//而且该函数中的this指向的是HTMLDocument对象,参数是jQuery对象,也就是构造函数,源码readyList.resolveWith(document,[jQuery])
$(function(args)
{
alert("ready"+this+"->"+args);
})
测试2:(调用jQuery.holdReady延迟)
//这时候的readyWait已经是2了,所以wait !== true && --jQuery.readyWait > 0是false,最后就是不执行!
//记住:这时候的jQuery.readyWait已经是1了,因为已经自减了!
//如何解除禁止呢?
jQuery.holdReady(1);
$(document).ready(function(){
alert("ready");
});
//这种方式调用以后,还要通过triggerHander来触发文档的ready事件!所以这个例子会执行两次!
//触发以后就要移除这个事件!因为ready事件不要多次调用!
//而且该函数中的this指向的是HTMLDocument对象,参数是jQuery对象,也就是构造函数
$(function(args)
{
alert("ready"+this+"->"+args);
})
//这时候相当于调用jQuery.ready( true );这时候 wait === true ? --jQuery.readyWait : jQuery.isReady是false
//这时候就会执行上面两次的ready事件!
jQuery.holdReady();
note:方法holdReady调用时候加入参数相当于进行添加一层禁止,如果不传入参数表示解除一层禁止!
ready方法源码:
var readyList;
jQuery.fn.ready = function( fn ) {
// Add the callback
jQuery.ready.promise().done( fn );
return this;
};
jQuery.ready方法:
jQuery.extend({
// Is the DOM ready to be used? Set to true once it occurs.
isReady: false,
// A counter to track how many items to wait for before
// the ready event fires. See #6781
readyWait: 1,
// Hold (or release) the ready event
holdReady: function( hold ) {
if ( hold ) {
jQuery.readyWait++;
} else {
jQuery.ready( true );
}
},
// Handle when the DOM is ready
ready: function( wait ) {
//如果readyWait不是0,或者isReady是false,那么什么也不做,继续等待!
// Abort if there are pending holds or we're already ready
if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
return;
}
//在IE中需要判断body是否存在,不存在那么等待!
// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
if ( !document.body ) {
return setTimeout( jQuery.ready );
}
//如果load事件或者contentLoad事件已经完成,那么把isReay设置为true!
// Remember that the DOM is ready
jQuery.isReady = true;
//一直等到readyWait为0
// If a normal DOM Ready event fired, decrement, and wait if need be
if ( wait !== true && --jQuery.readyWait > 0 ) {
return;
}
//如果已经readyWait是0,那么就会执行,也就是触发事件,最终调用Deferred对象的done方法
//上下文是document,第二个参数是jQuery对象!这时候外层的done已经执行了,也就说这个ready中的函数可以执行了!
//第一个参数是指向context所以ready中第一个参数是document对象,第二个jQuery就是参数也就是给ready函数传递的参数!
// If there are functions bound, to execute
readyList.resolveWith( document, [ jQuery ] );
//还要调用ready事件函数!
// Trigger any bound ready events
if ( jQuery.fn.triggerHandler ) {
jQuery( document ).triggerHandler( "ready" );
jQuery( document ).off( "ready" );
}
}
});
detach源码:(DOMContentedLoaded或者load只要有一个触发就清除事件)
function detach() {
if ( document.addEventListener ) {
document.removeEventListener( "DOMContentLoaded", completed, false );
window.removeEventListener( "load", completed, false );
} else {
document.detachEvent( "onreadystatechange", completed );
window.detachEvent( "onload", completed );
}
}
complete完成的源码:(只要事件类型是event.type=load或者document.readystate是complete)
function completed() {
// readyState === "complete" is good enough for us to call the dom ready in oldIE
//如果触发了load或者complete事件就移除相应的事件!
if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) {
detach();
jQuery.ready();
}
}
jQuery.ready.promise对象:(其实返回的是一个promise对象,该对象没有状态改变的方法,其中有三个Callbacks对象)
jQuery.ready.promise = function( obj ) {
//这里面的readyList是全局变量,所以可以在多个方法中共享!而且执行一次过后就不会被再次执行了!
if ( !readyList ) {
readyList = jQuery.Deferred();
// Catch cases where $(document).ready() is called after the browser event has already occurred.
// we once tried to use readyState "interactive" here, but it caused issues like the one
// discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
if ( document.readyState === "complete" ) {
//如果DOM已经完成,直接调用jQuery.ready方法,但是setTimeout没有第二个参数!
//这是为了防止IE的!
// Handle it asynchronously to allow scripts the opportunity to delay ready
setTimeout( jQuery.ready );
// Standards-based browsers support DOMContentLoaded
} else if ( document.addEventListener ) {
//为什么要同时检测DOMContentLoaded和load,因为有些浏览器中会缓存load事件
//所以会先触发load,后触发DOMContentLoaded,所以看那个更快就行!
// Use the handy event callback
document.addEventListener( "DOMContentLoaded", completed, false );
// A fallback to window.onload, that will always work
window.addEventListener( "load", completed, false );
// If IE event model is used
} else {
// Ensure firing before onload, maybe late but safe also for iframes
document.attachEvent( "onreadystatechange", completed );
// A fallback to window.onload, that will always work
window.attachEvent( "onload", completed );
// If IE and not a frame
// continually check to see if the document is ready
var top = false;
try {
//获取window中的<iframe>或者<object>对象
//如果页面中没有相应的iframe或者object那么获取documentElement对象!
top = window.frameElement == null && document.documentElement;
} catch(e) {}
//还好ie有个特有的doScroll方法,当页面DOM未加载完成时,调用doScroll方法时,
//就会报错,反过来,只要一直间隔调用doScroll直到不报错,那就表示页面DOM加载完毕了,
//不管图片和iframe中的内容是否加载完毕,此法都有效。
if ( top && top.doScroll ) {
(function doScrollCheck() {
if ( !jQuery.isReady ) {
//没有ready的时候那么就会调用documentElement的doScroll()方法
//不断调用这个方法,如果报错了就每隔50毫秒调用一次,如果不报错
//表示DOM加载完成,这时候就只要把所有的事件移除,然后调用ready方法就可以了
//这就是IE6-8的DOMContentLoaded事件的结果方法!
try {
// Use the trick by Diego Perini
// http://javascript.nwbox.com/IEContentLoaded/
top.doScroll("left");
} catch(e) {
return setTimeout( doScrollCheck, 50 );
}
// detach all dom ready events
detach();
// and execute any waiting functions
jQuery.ready();
}
})();
}
}
}
//让返回的对象obj继承promise的所有的方法!
//所以该对象相当于一个promise对象,我们知道promise和Deferred是相同的,只是前者没有改变状态的方法,
//所以归根到底,这个promise方法就是对应于一个$.Callbacks对象,而这个promise的done方法对应于其中的add方法!
//所以就是把所有的事件放在内部的一个list中,这个list中的所有函数要通过resolve或者resolveWith调用!
return readyList.promise( obj );
};
下面给出几个holdReady的例子:
例1:
$.holdReady(true);
$(function()
{
alert("x");//不会马上打印x,因为已经holdReady了!
})
例2:(假如a.js中alert(1),为了先打印1,后打印2,怎么做)
$.holdReady(true);
$.getScript("a.js",function(){ $.holdReady(false);})//释放
$(function(){alert(2)})//这时候就会先弹出1(a.js中打印1),后弹出2
例3:要等到所有的holdReady都减少为1.因为每次只是释放一次
$.holdReady(true);
$.getScript("a.js",function(){ $.holdReady(false);})//释放
$.holdReady(true);
$.getScript("b.js",function(){ $.holdReady(false);})//释放
$.holdReady(true);
$.getScript("c.js",function(){ $.holdReady(false);})//释放
$(function(){alert("xxxx");})
// 这时候readyWait就是3了!必须释放三次,也就是a.js,b.js.c.js全部加载完成才行!
总结:
(1)如果传入了参数那么直接判断jQuery.readyWait的值,如果没有传入参数那么首先判断jQuery.isReady的值,然后判断jQuery.readyWait的值!
(2)window.frameElement可以获取页面中的iframe或者object对象。
(3)要理解到,如果在页面中绑定了多个ready事件,其实是在Deferred对象所对应的Callbacks集合中添加了多个回调函数,所以当你resolve的时候多个方法都会调用!
(4)IE的doScroll方法,当页面DOM未加载完成时,调用doScroll方法时,就会报错,反过来,只要一直间隔调用doScroll直到不报错,那就表示页面DOM加载完毕了,不管图片和iframe中的内容是否加载完毕,此法都有效。
(5)document对象有DOMContentLoaded事件,window有onload事件。如果用attachEvent就要用document的onreadyStateChange进行监听,而window还是用load事件!