ready、queue放在一块写,没有特殊的意思,只是相对来说它俩可能源码是最简单的了。ready是在dom加载完成后,以最快速度触发,很实用。queue是队列,比如动画的顺序触发就是通过默认队列’fx’处理的。
(本文采用 1.12.0 版本进行讲解,用 #number 来标注行号)
ready
很多时候,我们需要尽快的加载一个函数,如果里面含有操作dom的逻辑,那么最好在dom刚刚加载完成时调用。window的load事件会在页面中的一切都加载完毕时(图像、js文件、css文件、iframe等外部资源)触发,可能会因外部资源过多而过迟触发。
DOMContentLoaded
:IE9+、Firefox、Chrome、Safari3.1+、Opera9+
html5规范指定的标准事件,在document上,在形成完整的dom树后就会触发(不理会图像、js文件、css文件等是否下载完毕)。
readystatechange
:IE、Firfox4+、Opera
这个事件的目的是提供与文档或元素的加载状态相关的信息,但这个事件的行为有时候很难预料。支持该事件的每个对象都有一个readyState属性,可能包含下列5个值中的一个。
uninitialized(未初始化):对象存在但尚未初始化
loading(正在加载):对象加载数据完成
interactive(交互):可以操作对象了,但还没有完全加载
complete(完成):对象已经加载完成
对document而言,值为”interactive”的readyState会在与DOMContentLoaded
大致相同时刻触发readystatechange
(行为难料,该阶段既可能早于也可能晚于complete阶段,jq上报告了一个interactive的bug,所以源码中用的complete)。而且在包含较少或较小的外部资源的页面中,readystatechange
有可能晚于load事件,因此优先使用DOMContentLoaded
。
jQuery思路
jq可以通过$(xx).ready(fn)
指定dom加载完后需要尽快调用的事件。我们知道事件一旦错过了监听,就不会再触发,$().ready()增加了递延支持,这里自然要使用'once memory'
的观察者模型,Callback、Deferred对象均可,源码中是一个Deferred对象,同时挂载在变量readyList上。
// #3539
jQuery.fn.ready = function( fn ) {
// jQuery.ready.promise() 为deferred对象内的promise对象(即readyList.promise())
jQuery.ready.promise().done( fn );
// 链式
return this;
};
有了promise对象,需要dom加载完后,尽快的resolve这个promise。判断加载完的方式,就是首先判断是否已经是加载完成状态,如果不是优先使用DOMContentLoaded事件,IE6-8用readystatechange,都要用load事件保底,保证一定触发。由于readystatechange为complete时机诡异有时甚至慢于load,IE低版本可以用定时器反复document.documentElement.doScroll('left')
判断,只有dom加载完成调用该方法才不报错,从而实现尽快的触发。
jQuery是富有极客精神的,绑定的触发函数调用一次后就不再有用,因此触发函数中不仅能resolve那个promise,还会自动解绑触发函数(方法detach()
),这样比如readystatechange、load多事件不会重复触发,同时节省内存。当然doScroll方法是setTimeout完成的,如果被readystatechange抢先触发,需要有变量能告知他取消操作,源码中是jQuery.isReady
。
触发函数->completed()
= 解绑触发函数->detach()
+ resolve那个promise->jQuery.ready()
jq中增加了holdReady(true)
功能,能够延缓promise的触发,holdReady()
不带参数(即jQuery.ready(true))则消减延迟次数,readyWait
初始为1,减至0触发。由于doScroll靠jQuery.isReady防止重复触发,因此即使暂缓jQuery.ready()也要能正常的设置jQuery.isReady = true
。jQuery.ready()不仅能触发promise,之后还会触发’ready’自定义事件。
思路整理
jQuery.fn.ready() -> 供外部使用,向promise上绑定待执行函数
jQuery.ready.promise() -> 生成单例promise,绑定事件触发completed()
complete() -> 解绑触发函数`detach()` + 无需等待时resolve那个promise`jQuery.ready()`
[源码]
// #3536
// readyList.promise() === jQuery.ready.promise()
var readyList;
jQuery.fn.ready = function( fn ) {
// promise后添加回调
jQuery.ready.promise().done( fn );
return this; // 链式
};
jQuery.extend( {
// doScroll需借此判断防止重复触发
isReady: false,
// 需要几次jQuery.ready()调用,才会触发promise和自定义ready事件
readyWait: 1,
holdReady: function( hold ) {
if ( hold ) {
//