今天在阅读网上一些模拟Jq的ready方法时,发现一些小细节,就是网上的ready事件大部分都是在onload事件执行后加载,而jquery确能在onload加载前,dom加载完后执行,一直不了解,基于对网上的一些方法逻辑不了解,所以去看了《jquery源代码研究(ready函数) 》这篇文章后自己写入如下代码(已有详细说明):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![ExpandedBlockStart.gif](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
<!
DOCTYPE html PUBLIC
"
-//W3C//DTD XHTML 1.0 Transitional//EN
"
"
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd
"
>
< html xmlns = " http://www.w3.org/1999/xhtml " >
< head >
< meta http - equiv = " Content-Type " content = " text/html; charset=utf-8 " / >
< title > document.ready < / title>
< script type = " text/javascript " src = " js/jquery-1.3.2.js " >< / script>
< script type = " text/javascript " >
var Darren;
( function (){
var isReady = false ; // 是否已经加载完毕
var readBound = false ; // 判断是否已经调用过循环事件
var readylist = []; // 把需要执行的方法先暂存在这个数组里
// 判断浏览器,该方法来自Cloudgamer JavaScript Library v0.1
var Browser = ( function (ua){
var b = {
msie: / msie / .test(ua) && ! / opera / .test(ua),
opera: / opera / .test(ua),
safari: / webkit / .test(ua) && ! / chrome / .test(ua),
firefox: / firefox / .test(ua),
chrome: / chrome / .test(ua)
};
var vMark = "" ;
for ( var i in b) {
if (b[i]) {
vMark = i;
}
}
if (b.safari) {
vMark = " version " ;
}
b.version = RegExp( " (?: " + vMark + " )[\\/: ]([\\d.]+) " ).test(ua) ? RegExp.$ 1 : " 0 " ;
b.ie = b.msie;
b.ie6 = b.msie && parseInt(b.version) == 6 ;
b.ie7 = b.msie && parseInt(b.version) == 7 ;
b.ie8 = b.msie && parseInt(b.version) == 8 ;
return b;
})(window.navigator.userAgent.toLowerCase());
function bindReady()
{
if (readBound){ // 保证bindReady方法只执行一遍
return ;
}
readBound = true ;
// For IE并且不是嵌套在frame中
if (Browser.msie && window == top)
{
( function (){
if (isReady) {
return ;
}
try {
document.documentElement.doScroll( " left " ); // 如果没加载dom完毕这个会报错
}
catch (error) {
setTimeout(arguments.callee, 0 ); // 循环调用父函数,也就是ready方法
return ;
}
Test.Done();
})();
} else if (Browser.firefox) // For FF
{
document.addEventListener( " DOMContentLoaded " , Test.Done, false );
}
}
var Test = {
ready: function (fn){
bindReady(); // 判断是否加载完毕
if (isReady)
{
fn.call(document); // 加载完毕,直接调用
} else {
readylist.push(fn); // 如果还没加载完成则将该方法暂存到readylist数组中,以便以后调用
}
return this ;
}
};
// 静态方法:加载完毕执行
Test.Done = function (){
if ( ! isReady) {
isReady = true ;
}
readylist[ 0 ].call(document);
}
Darren = Test;
})();
// 测试
Darren.ready( function (){
alert( " my " );
document.getElementById( " test " ).innerHTML = " haha " // 成功读取dom
});
$( function (){alert( " jq " )});
window.onload = function (){alert( " default " )}
< / script>
< / head>
< body >
< div id = " test " > test < / div>
< / body>
< / html>
< html xmlns = " http://www.w3.org/1999/xhtml " >
< head >
< meta http - equiv = " Content-Type " content = " text/html; charset=utf-8 " / >
< title > document.ready < / title>
< script type = " text/javascript " src = " js/jquery-1.3.2.js " >< / script>
< script type = " text/javascript " >
var Darren;
( function (){
var isReady = false ; // 是否已经加载完毕
var readBound = false ; // 判断是否已经调用过循环事件
var readylist = []; // 把需要执行的方法先暂存在这个数组里
// 判断浏览器,该方法来自Cloudgamer JavaScript Library v0.1
var Browser = ( function (ua){
var b = {
msie: / msie / .test(ua) && ! / opera / .test(ua),
opera: / opera / .test(ua),
safari: / webkit / .test(ua) && ! / chrome / .test(ua),
firefox: / firefox / .test(ua),
chrome: / chrome / .test(ua)
};
var vMark = "" ;
for ( var i in b) {
if (b[i]) {
vMark = i;
}
}
if (b.safari) {
vMark = " version " ;
}
b.version = RegExp( " (?: " + vMark + " )[\\/: ]([\\d.]+) " ).test(ua) ? RegExp.$ 1 : " 0 " ;
b.ie = b.msie;
b.ie6 = b.msie && parseInt(b.version) == 6 ;
b.ie7 = b.msie && parseInt(b.version) == 7 ;
b.ie8 = b.msie && parseInt(b.version) == 8 ;
return b;
})(window.navigator.userAgent.toLowerCase());
function bindReady()
{
if (readBound){ // 保证bindReady方法只执行一遍
return ;
}
readBound = true ;
// For IE并且不是嵌套在frame中
if (Browser.msie && window == top)
{
( function (){
if (isReady) {
return ;
}
try {
document.documentElement.doScroll( " left " ); // 如果没加载dom完毕这个会报错
}
catch (error) {
setTimeout(arguments.callee, 0 ); // 循环调用父函数,也就是ready方法
return ;
}
Test.Done();
})();
} else if (Browser.firefox) // For FF
{
document.addEventListener( " DOMContentLoaded " , Test.Done, false );
}
}
var Test = {
ready: function (fn){
bindReady(); // 判断是否加载完毕
if (isReady)
{
fn.call(document); // 加载完毕,直接调用
} else {
readylist.push(fn); // 如果还没加载完成则将该方法暂存到readylist数组中,以便以后调用
}
return this ;
}
};
// 静态方法:加载完毕执行
Test.Done = function (){
if ( ! isReady) {
isReady = true ;
}
readylist[ 0 ].call(document);
}
Darren = Test;
})();
// 测试
Darren.ready( function (){
alert( " my " );
document.getElementById( " test " ).innerHTML = " haha " // 成功读取dom
});
$( function (){alert( " jq " )});
window.onload = function (){alert( " default " )}
< / script>
< / head>
< body >
< div id = " test " > test < / div>
< / body>
< / html>
由于要和jq做对比,所以测试时候需要导入jq库。函数本身是没有调用jq的,请放心引用。
代码我通过封装完成,直接Darren.ready(fn)就可执行。
后来通过测试还是出现一个奇怪的问题:在FF下的执行顺序是jq -> my -> load 。也就是说我这个函数能够在onload事件执行前触发,但会晚于jq的ready。对这个还是比较满意。
但是在IE下测试居然是:jq -> load -> my。也就是 我的这个函数虽然能够把代码提前,但是还是在onload事件执行后触发的,百思不得其解。
完同志们解答下如何实现onload之前执行,jq又是怎么实现的,我完全模拟jq的结构,但是还是不能达到目的,难道中间有漏?