深入分析jQuery的ready函数是如何工作的(工作原理)
本文深入分析jQuery的ready函数是如何工作的。分享给大家供大家参考,具体如下:
jQuery是一个伟大的脚本库,由JohnResig在2006年1月的BarCampNYC上释出第一个版本。你可以在http://jquery.com/下载到最新版本,截止本文发布为止已更新到jQuery2.1.4版。这里以jQuery1.8.3为例分析。
学习jQuery有许多途径,我们今天从jQuery的ready函数开始。本例中的代码都来自于jQuery脚本库。
如果你使用过jQuery,就必然使用过ready函数,它用来注册当页面准备好之后可以执行的函数。
问题来啦,我们的页面什么时候准备好了呢?
1.onload事件
最基本的处理方式就是页面的onload事件,我们在处理这个事件的时候,可以有多种方式,即可以通过HTML方式,直接写在body元素的开始标记中,也可以使用事件注册的方式来使用,这又可以分为DOM0方式和DOM2方式。再考虑到浏览器的兼容性,使用DOM2方式写出来,如下所示。
if(document.addEventListener){ //Afallbacktowindow.onload,thatwillalwayswork window.addEventListener("load",jQuery.ready,false); //IfIEeventmodelisused }else{ //Afallbacktowindow.onload,thatwillalwayswork window.attachEvent("onload",jQuery.ready); }
2.DOMContentLoaded事件
不过onload事件要等到所有页面元素加载完成才会触发,包括页面上的图片等等。如果网页上有大量的图片,效果可想而知,用户可能在没有看到图片的时候,就已经开始操作页面了,而这时我们的页面还没有初始化,事件还没有注册上,这岂不是太晚了!
除了大家熟知的onload事件之外,与DOM中的onload事件相近的,我们还有DOMContentLoaded事件可以考虑,基于标准的浏览器支持这个事件, 当所有DOM解析完以后会触发这个事件。
这样,对于基于标准的浏览器来说,我们还可以注册这个事件的处理。这样,我们可能更早地捕获到加载完成的事件。
if(document.addEventListener){ //Usethehandyeventcallback document.addEventListener("DOMContentLoaded",DOMContentLoaded,false); //Afallbacktowindow.onload,thatwillalwayswork window.addEventListener("load",jQuery.ready,false); }
3.onreadystatechange事件
不标准的浏览器怎么办呢?
如果浏览器存在document.onreadystatechange事件,当该事件触发时,如果document.readyState=complete的时候,可视为DOM树已经载入。
不过,这个事件不太可靠,比如当页面中存在图片的时候,可能反而在onload事件之后才能触发,换言之,它只能正确地执行于页面不包含二进制资源或非常少或者被缓存时作为一个备选吧。
if(document.addEventListener){ //Usethehandyeventcallback document.addEventListener("DOMContentLoaded",DOMContentLoaded,false); //Afallbacktowindow.onload,thatwillalwayswork window.addEventListener("load",jQuery.ready,false); //IfIEeventmodelisused }else{ //Ensurefiringbeforeonload,maybelatebutsafealsoforiframes document.attachEvent("onreadystatechange",DOMContentLoaded); //Afallbacktowindow.onload,thatwillalwayswork window.attachEvent("onload",jQuery.ready); }
DOMContentLoaded函数在做什么呢?最终还是要调用jQuery.ready函数。
DOMContentLoaded=function(){ if(document.addEventListener){ document.removeEventListener("DOMContentLoaded",DOMContentLoaded,false); jQuery.ready(); }elseif(document.readyState==="complete"){ //we'reherebecausereadyState==="complete"inoldIE //whichisgoodenoughforustocallthedomready! document.detachEvent("onreadystatechange",DOMContentLoaded); jQuery.ready(); } }
4.doScroll检测法
MSDN关于JScript的一个方法有段不起眼的话,当页面DOM未加载完成时,调用doScroll方法时,会产生异常。那么我们反过来用,如果不异常,那么就是页面DOM加载完毕了!
DiegoPerini在2007年的时候,报告了一种检测IE是否加载完成的方式,使用doScroll方法调用。详细的说明见这里。
原理是对于IE在非iframe内时,只有不断地通过能否执行doScroll判断DOM是否加载完毕。在本例中每间隔50毫秒尝试去执行doScroll,注意,由于页面没有加载完成的时候,调用doScroll会导致异常,所以使用了try-catch来捕获异常。
(functiondoScrollCheck(){ if(!jQuery.isReady){ try{ //UsethetrickbyDiegoPerini //http://javascript.nwbox.com/IEContentLoaded/ top.doScroll("left"); }catch(e){ returnsetTimeout(doScrollCheck,50); } //andexecuteanywaitingfunctions jQuery.ready(); } })();
5.document.readyState状态
如果我们注册ready函数的时间点太晚了,页面已经加载完成之后,我们才注册自己的ready函数,那就用不着上面的层层检查了,直接看看当前页面的readyState就可以了,如果已经是complete,那就可以直接执行我们准备注册的ready函数了。不过ChrisS报告了一个很特别的错误情况,我们需要延迟一下执行。
setTimeout经常被用来做网页上的定时器,允许为它指定一个毫秒数作为间隔执行的时间。当被启动的程序需要在非常短的时间内运行,我们就会给她指定一个很小的时间数,或者需要马上执行的话,我们甚至把这个毫秒数设置为0,但事实上,setTimeout有一个最小执行时间,当指定的时间小于该时间时,浏览器会用最小允许的时间作为setTimeout的时间间隔,也就是说即使我们把setTimeout的毫秒数设置为0,被调用的程序也没有马上启动。
这个最小的时间间隔是多少呢?这和浏览器及操作系统有关。在JohnResig的新书《Javascript忍者的秘密》一书中提到
Browsersallhavea10msminimumdelayonOSXanda(approximately)15msdelayonWindows.(在苹果机上的最小时间间隔是10毫秒,在Windows系统上的最小时间间隔大约是15毫秒),另外,MDC中关于setTimeout的介绍中也提到,Firefox中定义的最小时间间隔(DOM_MIN_TIMEOUT_VALUE)是10毫秒,HTML5定义的最小时间间隔是4毫秒。既然规范都是这样写的,那看来使用setTimeout是没办法再把这个最小时间间隔缩短了。
这样,通过设置为1,我们可以让程序在浏览器支持的最小时间间隔之后执行了。
//Catchcaseswhere$(document).ready()iscalledafterthebrowsereventhasalreadyoccurred. //weoncetriedtousereadyState"interactive"here,butitcausedissuesliketheone //discoveredbyChrisShere:http://bugs.jquery.com/ticket/12282#comment:15 if(document.readyState==="complete"){ //延迟1毫秒之后,执行ready函数 setTimeout(jQuery.ready,1); }
6.完整的代码
在jQuery中完整的代码如下所示。位于jQuery1.8.3源代码的#842行。
jQuery.ready.promise=function(obj){ if(!readyList){ readyList=jQuery.Deferred(); //Catchcaseswhere$(document).ready()iscalledafterthebrowsereventhasalreadyoccurred. //weoncetriedtousereadyState"interactive"here,butitcausedissuesliketheone //discoveredbyChrisShere:http://bugs.jquery.com/ticket/12282#comment:15 if(document.readyState==="complete"){ //Handleitasynchronouslytoallowscriptstheopportunitytodelayready setTimeout(jQuery.ready,1); //Standards-basedbrowserssupportDOMContentLoaded }elseif(document.addEventListener){ //Usethehandyeventcallback document.addEventListener("DOMContentLoaded",DOMContentLoaded,false); //Afallbacktowindow.onload,thatwillalwayswork window.addEventListener("load",jQuery.ready,false); //IfIEeventmodelisused }else{ //Ensurefiringbeforeonload,maybelatebutsafealsoforiframes document.attachEvent("onreadystatechange",DOMContentLoaded); //Afallbacktowindow.onload,thatwillalwayswork window.attachEvent("onload",jQuery.ready); //IfIEandnotaframe //continuallychecktoseeifthedocumentisready vartop=false; try{ top=window.frameElement==null&&document.documentElement; }catch(e){} if(top&&top.doScroll){ (functiondoScrollCheck(){ if(!jQuery.isReady){ try{ //UsethetrickbyDiegoPerini //http://javascript.nwbox.com/IEContentLoaded/ top.doScroll("left"); }catch(e){ returnsetTimeout(doScrollCheck,50); } //andexecuteanywaitingfunctions jQuery.ready(); } })(); } } } returnreadyList.promise(obj); };
那么,又是谁来调用呢?当然是需要的时候,在我们调用ready函数的时候,才需要注册这些判断页面是否完全加载的处理,这段代码在1.8.3中位于代码的#244行,如下所示:
ready:function(fn){ //Addthecallback jQuery.ready.promise().done(fn); returnthis; }
在页面上引用jQuery脚本库之后,执行了jQuery的初始化函数,初始化函数中创建了ready函数。我们在通过ready函数注册事件处理之前,jQuery完成了页面检测代码的注册。这样。当页面完全加载之后,我们注册的函数就被调用了。
补充:jqueryready简写模式
Jqueryready函数:
$(document).ready(function(){ alert('iamready'); } );
可简写为:
$(function(){ alert("iamin"); } );
希望本文所述对大家jQuery程序设计有所帮助。