JavaScript模拟jQuery Ready方法实现及IE与Firefox执行顺序差异分析

作者:闭包旅人时间:2026-10-24 11:22:00浏览:277

本文详细解析了如何使用原生JavaScript模拟jQuery的Ready方法,重点探讨了在Firefox和IE浏览器中DOM加载完成后的执行顺序差异。通过对比不同浏览器的实现机制,分析了doScroll检测与DOMContentLoaded事件的适用场景,并指出了在IE环境下可能出现的执行时序问题,提供了参考实现代码。

JavaScript模拟jQuery Ready方法实现

在DOM加载完成后执行脚本是前端开发中的常见需求。由于对网络上现有方法的逻辑不够清晰,笔者阅读了《jquery源代码研究(ready函数)》一文后,尝试编写了如下代码(代码中已包含详细说明)。


具体实现代码如下:

<!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=gb2312" />
<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>

浏览器执行顺序差异分析

为了与jQuery进行对比测试,代码中引入了jQuery库。需要注意的是,上述封装函数本身并未依赖jQuery,因此可以安全引用。



代码经过封装后,直接调用Darren.ready(fn)即可执行自定义逻辑。



然而,测试过程中发现了一个奇怪的现象:在Firefox浏览器中,执行顺序为jQuery的Ready -> 自定义my函数 -> window.onload。这表明该函数能够在onload事件之前触发,但执行时机晚于jQuery的Ready。对此结果笔者表示满意。



但在IE浏览器中测试时,执行顺序却变成了:jQuery的Ready -> window.onload -> 自定义my函数。这意味着虽然该函数将代码执行提前到了onload之前,但在IE中它仍然晚于onload事件触发。这一结果令人困惑。



希望有高手解答如何实现真正在onload之前执行,以及jQuery具体是如何实现的。笔者完全模拟了jQuery的结构,但未能达到预期效果,怀疑是否有遗漏之处。



参考实现代码

此外,大家可以参考以下另一种实现代码:


具体实现代码如下:

var ready=function(readyCall) {
if(document.addEventListener)
document.addEventListener("DOMContentLoaded",function() {
document.removeEventListener("DOMContentLoaded",arguments.callee,false);
readyCall();
},false);
else if(document.attachEvent) {//for IE
if(document.documentElement.doScroll && window.self==window.top) {
(function() {
try {
document.documentElement.doScroll("left");
}catch(ex) {
setTimeout(arguments.callee,5);
return;
} 
readyCall();
})(); 
}else {//maybe late but also for iframes
document.attachEvent("onreadystatechange",function() {
if(document.readyState==="complete") {
document.detachEvent("onreadystatechange", arguments.callee);
readyCall();
} 
}); 
} 
} 
}
作者最新文章