jQuery源码分析之ready方法

来源:互联网 发布:大数据分析师培训课程 编辑:程序博客网 时间:2024/06/04 18:50

首先阅读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 callbackjQuery.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 #6781readyWait: 1,// Hold (or release) the ready eventholdReady: function( hold ) {if ( hold ) {jQuery.readyWait++;} else {jQuery.ready( true );}},// Handle when the DOM is readyready: function( wait ) {//如果readyWait不是0,或者isReady是false,那么什么也不做,继续等待!// Abort if there are pending holds or we're already readyif ( 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 readyjQuery.isReady = true;         //一直等到readyWait为0// If a normal DOM Ready event fired, decrement, and wait if need beif ( 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 executereadyList.resolveWith( document, [ jQuery ] );         //还要调用ready事件函数!// Trigger any bound ready eventsif ( 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:15if ( document.readyState === "complete" ) {//如果DOM已经完成,直接调用jQuery.ready方法,但是setTimeout没有第二个参数!//这是为了防止IE的!// Handle it asynchronously to allow scripts the opportunity to delay readysetTimeout( jQuery.ready );// Standards-based browsers support DOMContentLoaded} else if ( document.addEventListener ) {//为什么要同时检测DOMContentLoaded和load,因为有些浏览器中会缓存load事件//所以会先触发load,后触发DOMContentLoaded,所以看那个更快就行!// Use the handy event callbackdocument.addEventListener( "DOMContentLoaded", completed, false );// A fallback to window.onload, that will always workwindow.addEventListener( "load", completed, false );// If IE event model is used} else {// Ensure firing before onload, maybe late but safe also for iframesdocument.attachEvent( "onreadystatechange", completed );// A fallback to window.onload, that will always workwindow.attachEvent( "onload", completed );// If IE and not a frame// continually check to see if the document is readyvar 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 eventsdetach();// and execute any waiting functionsjQuery.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事件!

0 0
原创粉丝点击