您的位置:首页 > Web前端 > JQuery

jQuery deferred应用dom加载完毕详细源码分析(三)

2011-12-05 23:41 585 查看
我承认上章ajax部分写得不好,不要怪我,它的ajax代码太多了,而且跨越大,方法跳跃多,实在不好排版与讲解,但如果你真正想研究源码并且仔细读了得话,你的

收获应该会很大,至少你明白了js的ajax是怎么回事.不懂得可以继续参阅ajaxDeferred ,其实我更希望你把deferred搞懂了在看这部分.

好吧,言归正传,这次我讲得是$(function(){console.log("dom ready")}); 多么简单的一段代码,但它是在每个浏览器运行的呢?

还是先来个全局展望,会用到以下几个方法,这里你会知道jQuery是真正的hold住哥,因为他要hold dom ready event holdReady,一个神奇的方法

$(fn) --> $.fn.ready(fn) -->$.bindReady() --> $.ready(wait) <--> $.holdReady(hold)

下面这个是init函数的一部分代码,就是判断$里面的参数是函数

// 处理: $(function)   简写 $(document).ready(function)
if (jQuery.isFunction(selector)) {
return rootjQuery.ready(selector);        ->所以这里你应该明白了为什么可以$(fn) 替代$(document).ready(fn)
}                        --> rootjQuery=$(document)

那$(document).ready(fn)后又怎么办呢? 难道$(document)不是实例化的一个jQuery对象,它当然又要去调用$.fn.ready的函数


好,来到了$.fn.ready,好吧这个函数并没有管如何处罚dom ready事件,而只是关心callbacks函数队列fn ,意思就是你$(fn1),$(fn2),

它靠得还是全局的readyList,然后这个readyList是一个Deferred对象,知道它的重要了吧!

/*$(fn)--> $(document).ready(fn) 为什么是$(document)而不是其他$("body")呢?
* $(function () {console.log(this === document); }); -->true
*/
ready: function (fn) {
// Attach the listeners
jQuery.bindReady();

//增加$(fn)里的回调函数 以便domready触发时候   readyList.resolveWith(document,[jQuery]); 这里readyList是全局的变量
readyList.done(fn);

return this;
},

--->这里可以看到,继续去调用$.bindReady(),这里不关系dom ready是如何触发的


来到bindReady函数,根据不同的函数支持情况进行了不同的处理,在这里多少能学点浏览器兼容的知识了吧,

其实学了jQuery,神马浏览器兼容知识你都学到了.但是这个函数真正关心得只是事件的添加,不关心事件如何触发,,

这里最终的callback触发还是交给了$.ready 这里会有一个DOMContentLoaded公用的函数

/* 从这里可以看出来其实$(function(){})如果页面存在很多的话,也不太会影响性能,因为这个$.bindReady只执行一次
* 而以后$(fn)进来的函数,都通过deferred的done函数添加进来,只要这个对象resolve了,立马后面所有的fn立即执行
*/
bindReady: function () {
//如果readyList存在,直接返回,证明这里已经创建了一个deferred对象
if (readyList) {
return;
}
//创建jQuery 私有的deferred
readyList = jQuery._Deferred();

//确保browser event已经触发过后,$(document).ready()也将会被调用
//这个状态表示Fully loaded,document已经完全加载了
if (document.readyState === "complete") {
-->jQuery代码总担心出错,但是你不要担心你的回调会因为多处的被添加到事件添加器中去而被多次触发
--> $.ready会帮你处理的 ~_~
// Handle it asynchronously to allow scripts the opportunity to delay ready
return setTimeout(jQuery.ready, 1);
}

// Mozilla, Opera and webkit nightlies currently support this event
if (document.addEventListener) {
//支持DOMContentLoaded事件了,这样就简单了
document.addEventListener("DOMContentLoaded", DOMContentLoaded, false);

// A fallback to window.onload, that will always work  ->jQuery代码总担心出错,
//但是你不要担心,这样会不会多次触发你的回调函数,告诉你当然不会!
window.addEventListener("load", jQuery.ready, false);

// If IE event model is used
} else if (document.attachEvent) {
//这里主要担心页面内有iframe,所以使用onreadystatechange触发
document.attachEvent("onreadystatechange", DOMContentLoaded);

// A fallback to window.onload, that will always work
window.attachEvent("onload", jQuery.ready);

//如果是ie并且页面window是toplevel的,继续检查              -> ie关心的代码,doScrollCheck()又是神马呢?
//MDN: window.frameElement == null if the window is top-level
var toplevel = false;

try {
toplevel = window.frameElement == null;
} catch (e) { }

if (document.documentElement.doScroll && toplevel) {
doScrollCheck();
}
}
},

//原来DOMContentLoaded还是在这里,最后都是交给了$.ready()处理,而DOMContentLoaded并没有做什么,只是又一次的去掉事件监听
if (document.addEventListener) {
DOMContentLoaded = function () {
document.removeEventListener("DOMContentLoaded", DOMContentLoaded, false);
jQuery.ready();
};

} else if (document.attachEvent) {
DOMContentLoaded = function () {
// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
if (document.readyState === "complete") {
document.detachEvent("onreadystatechange", DOMContentLoaded);
jQuery.ready();
}
};
}

/* 这里只针对ie,当页面DOM未加载完成时,调用doScroll方法时,会产生异常。
* 那么我们反过来用,如果不异常,那么就是页面DOM加载完毕了!
* 这里的想法真的很淫荡!
*/
function doScrollCheck() {
if (jQuery.isReady) {
return;
}
try {
// If IE is used, use the trick by Diego Perini
// http://javascript.nwbox.com/IEContentLoaded/ document.documentElement.doScroll("left");
} catch (e) {
setTimeout(doScrollCheck, 1);
return;
}

//直到没有异常的时候,执行$.ready()
jQuery.ready();
}
-->你最终会发现,其实都是交给了$.ready()处理,不管你怎么触发事件 ^_^


好吧,最后所有的重头戏都落到了ready,最后的callbacks都是靠他触发的,而他里面的readyList又一是一个deferred对象,你懂了吧,他要

resolveWith(document,[jQuery]),你知道这个document意味着什么吗?$(function(){console.log(this===document)}) ~_~.

/* $.holdReady 原来jQuery才是真正的hold住哥,这里它要hold domReady事件,
* $.holdReady(true)--> 延迟hold一次,延迟domReady事件触发
* $.holdReady(false) --> 释放hold一次,如果释放后$.readyWait=0,则触发domReady事件
* 整个函数的功能就是延迟domReady事件,比如你需要动态加载一个jQuery插件神马的,然后你又要使用这个插件
* $.holdReady(true);$.getScript("myplugin.js", function() { $.holdReady(false);});
->使用上面这个语句整个场面都hold住了,加载完成后释放
*/
holdReady: function (hold) {                           -->这里你总算看到了神马是hold住哥
if (hold) {
jQuery.readyWait++;
} else {
jQuery.ready(true);
}
},
/*归根揭底,dom树加载完成都是靠它来触发callbacks,只是不同的浏览器通过不同的方式来触发$.ready()
*/
ready: function (wait) {
// 释放hold状态或者dom 事件触发
if ((wait === true && ! --jQuery.readyWait) || (wait !== true && !jQuery.isReady)) {
// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
if (!document.body) {
return setTimeout(jQuery.ready, 1);
}

// 表面dom树已加载完毕
jQuery.isReady = true;

//if 一个正常的dom函数事件触发了,但是还有hold的状态,返回把,先不触发
if (wait !== true && --jQuery.readyWait > 0) {
return;
}
//触发绑定在 readyList.done(fn);中得函数
//函数原来还是在这里触发的
readyList.resolveWith(document, [jQuery]);

//触发任何绑在document ready的事件
// $(document).bind({"ready": function () {alert("document ready");} });
if (jQuery.fn.trigger) {
jQuery(document).trigger("ready").unbind("ready");
}
}
},


这上面有个2B的代码 $(document).bind("ready",function(){..})不知道哪个2B青年会用这个方法...

好吧,如果看懂了上面的代码,你完全可以写一个自己的$(function(){}) 而不必为了jQuery为了符合大众的口味去这样实现!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: