jQuery源码学习9——DOMReady加载
先将和ready相关的代码都归纳出来
function jQuery(a,c) { if ( a && a.constructor == Function && jQuery.fn.ready ){ return jQuery(document).ready(a); } } jQuery.fn.extend({ ready: function(f) { if ( jQuery.isReady ) f.apply( document ); else { jQuery.readyList.push( f ); } return this; } }); jQuery.extend({ isReady: false, readyList: [], ready: function() { if ( !jQuery.isReady ) { jQuery.isReady = true; if ( jQuery.readyList ) { for ( var i = 0; i < jQuery.readyList.length; i++ ) jQuery.readyList[i].apply( document ); jQuery.readyList = null; } } } }); new function(){ if ( jQuery.browser.mozilla || jQuery.browser.opera ) { document.addEventListener( "DOMContentLoaded", jQuery.ready, false ); } else if ( jQuery.browser.msie ) { document.write("<scr" + "ipt id=__ie_init defer=true " + "src=//:><\/script>"); var script = document.getElementById("__ie_init"); script.onreadystatechange = function() { if ( this.readyState == "complete" ) jQuery.ready(); }; script = null; } else if ( jQuery.browser.safari ) { jQuery.safariTimer = setInterval(function(){ if ( document.readyState == "loaded" || document.readyState == "complete" ) { clearInterval( jQuery.safariTimer ); jQuery.safariTimer = null; jQuery.ready(); } }, 10); } jQuery.event.add( window, "load", jQuery.ready ); }
jQuery里面的初始化已经在document或者script上绑定了一些事件
FF和opera下,document的DOMContentLoaded完成之后触发jQuery.ready事件
IE下,script的onreadystatechange事件中script.readyState是complete的时候触发jQuery.ready事件
webkit内核下貌似是手动实现了一个onreadystatechange事件,开了一个定时器,每隔10ms监听一下document上的readyState是否为complete
如果是complete的话,触发jQuery.ready事件,顺便把这个定时器干掉
最后做了一个万全的方案,window对象的onload方法触发时,也就是页面中所有东西都加载完的时候触发jQuery.ready事件
通过上述分析可以发现不管在哪个浏览器下都有两次机会触发jQuery.ready事件
第一次就是在DOM结构加载完毕(DOMContentLoaded完毕或者readyState为complete)
第二次就是在页面所有资源加载完毕(window.onload)
这两次触发jQuery.ready是相互竞争的
如果不做任何处理,一定会重复两次去执行jQuery.ready
所以jQuery初始化的时候加了一个isReady的一个静态方法
初始值为false
在触发jQuery.ready的时候,先if判断了一下jQuery.ready的值
如果jQuery.ready的值是false的话,证明之前没有触发过jQuery.ready
jQuery.ready里面马上把jQuery.isReady变成true,代表触发过了
那第二次再触发jQuery.ready的时候里面再判断jQuery.isReady
变成了true的话就什么都不做了
我们关键看jQuery.ready是false,即之前没有触发过的情况
在这种情况下进一步判断jQuery.readyList
jQuery.readyList也是在jQuery初始化的时候定义的
初始值是一个空数组
我们在以$(function(){})或者$(document).ready(function(){})调用的时候都会在某个时机把里面的函数添加到jQuery.readyList中
数组不管空还是不空都是真,所以这个判断始终为真
进入if判断之后就循环jQuery.readyList里面的所有函数
依次执行每个函数
或许是考虑到jQuery.readyList在执行完以后就没什么用了,所以就把readyList直接赋值为null了
我们通过$(function(){})或者$(document).ready(function(){})的调用方式希望页面加载完毕触发里面的函数时
会调用jQuery.fn.ready这个方法
这个jQuery.fn.ready里面的实现虽然只有短短几行代码,但很值得分析一下
里面通过判断jQuery.isReady的值来执行不同的操作
前面分析到jQuery.isReady初始化为false
而且是在DOMContentLoaded/readyState=="complete"/onload任何一种完事了之后变成true的
在jQuery.fn.ready里面
jQuery.isReady是false的时候我们是把事件function添加到readyList队列里面
jQuery.isReady是true的时候我们直接执行了function
这里就不得不佩服jQuery作者英明的思路了
isReady是false的时候放在队列里面的方法在DOMContentLoaded/readyState=="complete"/onload完毕之后在jQuery.ready()中去挨个执行
isReady是true的时候,页面已经加载完毕,所以就直接执行function了
还是用例子说更直观一些,例如下面的代码
$(aaa); $(bbb); $(ccc); $(ddd); function aaa(){ alert(1); } function bbb(){ alert(2); } function ccc(){ alert(3); } function ddd(){ $(eee); } function eee(){ alert(4); }
最开始我们引入的jQuery执行的时候其实已经绑定了DOMContentLoaded/readyState=="complete"/onload这一系列事件
那从引入的jQuery文件执行完毕到这些事件触发并执行jQuery.ready之前js会接着往下执行$(aaa) $(bbb) $(ccc)...
假如执行到$(bbb)的时候
DOMContentLoaded/readyState=="complete"/onload里面中的一个触发了
那在jQuery.ready执行的时候jQuery.readyList里面就会有aaa bbb两个函数
为什么是这两个呢?
因为在DOMContentLoaded/readyState=="complete"/onload触发之前,每执行一次$(xxx)
判断之后发现jQuery.isReady是false,就会将aaa bbb这两个函数push到readyList中了
此后jQuery.isReady的值就是true了
再执行$(ccc) $(ddd)的话,判断之后发现jQuery.isReady是true
ccc和ddd就会直接执行
执行ddd的时候发现ddd里面又执行了$(eee)
同样jQuery.isReady仍然是true,所以还是会直接执行eee