代码改变世界

jQuery ready函数实现原理

2012-03-07 18:33  依水间  阅读(12773)  评论(0编辑  收藏  举报

jQuery是一套跨浏览器的JavaScript函式库,强化HTML与JavaScript之间的操作。由John Resig在2006年1月的BarCamp NYC上释出第一个版本。目前全球有28%的站台使用jQuery,是目前最受欢迎的JavaScript函式库。

jquery ready函数源代码:一般情况下都是设置body标签的onload监听window的load事件.但load事件是要在页面的元素全部加载完了才触发的,如果页面上图片较多或图片太大,就会导致初始化的代码未被执行的时候用户就做了其它操作了. Jquery库提供了一个非常方便好用的函数( $(selector).ready()),让我们可以在页面的dom加载完后就可以做相应的操作(当然,这还得看用户浏览器的支持).,而不用等待全部元素加载完成.例如: 
$(document).ready(function (){ alert('use in page script tag') }); 
$(document).ready(function (){ alert('use in import js file') }); 
现在让我们来研究一下这个函数的实现. 
原理: 
在jquery脚本加载的时候,会设置一个isReady的标记,监听DOMContentLoaded事件(这个不是什么浏览器都有的,不同浏览器,jquery运作方式不一样).当然遇到调用ready函数的时候,如果isReady未被设置,那就是说页面未加载完,就会把要执行的函数用一个数组缓存起来,当页面加载完后,再把缓存的函数一一执行. 
Jquery中的详细代码分析: 

代码如下:
ready: function(fn) { 
// 绑定监听器 
bindReady(); 
// 如果 DOM 加载完成 
if ( jQuery.isReady ) 
// 马上运行此函数 
fn.call( document, jQuery ); 
// 否则保存起来 
else 
// 把函数加入缓存数组中 
jQuery.readyList.push( function() { return fn.call(this, jQuery); } ); 
return this; 
}

让我们看看jquery如果实现不同浏览器dom加载完成的通知 bindReady()函数: 

代码如下:
var readyBound = false; 
function bindReady(){ 
if ( readyBound ) return; 
readyBound = true; 
// Mozilla,opera,webkitnightlies支持DOMContentLoaded事件 
if ( document.addEventListener && !jQuery.browser.opera) 
// 直接使用事件回调即可 
document.addEventListener( "DOMContentLoaded", jQuery.ready, false ); 
// 如果是ie并且不是嵌在frame中 
// 就需要不断地检查文档是否加载完 
if ( jQuery.browser.msie && window == top ) (function(){ 
if (jQuery.isReady) return; 
try { 
// 这个地方标记一下,在后面解析(1) 
document.documentElement.doScroll("left"); 
} catch( error ) { 
//// 这个地方标记一下,在后面解析(2) 
setTimeout( arguments.callee, 0 ); 
return; 

// and execute any waiting functions 
jQuery.ready(); 
})(); 
if ( jQuery.browser.opera ) 
document.addEventListener( "DOMContentLoaded", function () { 
if (jQuery.isReady) return; 
for (var i = 0; i < document.styleSheets.length; i++) // 标记(3) 
if (document.styleSheets[i].disabled) { 
setTimeout( arguments.callee, 0 ); 
return; 

// and execute any waiting functions 
jQuery.ready(); 
}, false); 
if ( jQuery.browser.safari ) { 
var numStyles; 
(function(){ 
if (jQuery.isReady) return; 
if ( document.readyState != "loaded" && document.readyState != "complete" ) { // 标记(4) 
setTimeout( arguments.callee, 0 ); 
return; 

if ( numStyles === undefined ) 
numStyles = jQuery("style, link[rel=stylesheet]").length; 
if ( document.styleSheets.length != numStyles ) { // 标记(5) 
setTimeout( arguments.callee, 0 ); 
return; 

// and execute any waiting functions 
jQuery.ready(); 
})(); 

// A fallback to window.onload, that will always work 
jQuery.event.add( window, "load", jQuery.ready ); // 标记(6) 

}

(1):这个主要是测出ie下的dom ready,原理在这里http://javascript.nwbox.com/IEContentLoaded/,利用在ie下.当dom未完成解析时,调用document的document.documentElement.doScroll(”left”)会出错这个小技巧便可得知dom有没有ready了. 
(2):setTimeout( arguments.callee, 0 )这句是表示延迟0秒调用,实际上它不会马上就调用,而是会尽可能快地调用,它告诉浏览器为当前任何挂起的事件运行完事件句柄并且完成了文档当前状态的更新后才调用. Arguments.callee即是外层的匿名函数,参数的调用者 
(3):这个地方你也许觉得奇怪,为什么不在mozilla那里一起处理呢? 原因就是opera的DOMContentLoaded事件发生后,其css样式是还没完全可用的,所以要特殊处理,就是判断每个css的tag都是不是enable了. 
(4),(5):safari中document.readyState的状态为loaded或complete时,css文件引入还未能确定是不是解析完了的,所以要通过判断其css文件数目 
(6):最后,如果上面的hack都不支持的话…就用最保险的load事件,保证能执行到初始化代码.