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

IE模拟addDOMLoadEvent和jQuery的ready实现

2013-05-22 23:25 465 查看
由于
window.onload
事件需要在页面所有内容(包括图片等)加载完后,才执行,但往往我们更希望在 DOM 一加载完就执行脚本。其实在现在大部分主流浏览器上(Firefox 3+,Opera 9+,Safari 3+,Chrome 2+)都提供了这一事件方法:
addDOMLoadEvent


document.addEventListener("DOMContentLoaded", init,false);

dom渲染完成,doScroll方法从不能执行到可执行,这样我们在IE中,利用
doScroll()
方法来模拟 addDOMLoadEvent 事件,现在主流的 JavaScript 框架(JQuery、YUI等)基本都采用的这一解决方案。

/*
*
* IEContentLoaded.js
*
* Author: Diego Perini (diego.perini at gmail.com) NWBOX S.r.l.
* Summary: DOMContentLoaded emulation for IE browsers
* Updated: 05/10/2007
* License: GPL/CC
* Version: TBD
*
*/
// @w    window对象的引用
// @fn   回调函数
function IEContentLoaded(w, fn) {
var d = w.document,
done = false,
// 只执行一次
init = function() {
if (!done) {
done = true;
fn();
}
}; // 循环执行,直到无错误
(function() {
try { // 一直抛出错误,直到dom渲染结束
d.documentElement.doScroll('left');
} catch(e) {
setTimeout(arguments.callee, 50);
return;
} // 没有错误,执行函数
init();
})(); // 双重保险,绑定事件
d.onreadystatechange = function() {
if (d.readyState == 'complete') {
d.onreadystatechange = null;
init();
}
};
}


参考于 http://www.planabc.net/2009/07/30/adddomloadevent/
jQuery1.4.4中ready实现

jQuery.fn = jQuery.prototype = {
// ...
ready: function( fn ) {
// 绑定上监听事件
jQuery.bindReady();

// 如果dom已经渲染
if ( jQuery.isReady ) {
// 立即执行
fn.call( document, jQuery );

// 否则,保存到缓冲队列,等上面的监听事件触发时,再全部执行
} else if ( readyList ) {
// 将回调增加到队列中
readyList.push( fn );
}

return this;
},
// ...
};
jQuery.extend({
// ...
// dom是否渲染完成标志
isReady: false,

// 一个计数器,用于跟踪在ready事件出发前的等待次数
// 预留,支持于更高版本jQuery提供的ready等待方法
readyWait: 1,

// 文档加载完毕句柄,dom渲染完成后执行
ready: function( wait ) {
// A third-party is pushing the ready event forwards
if ( wait === true ) {
jQuery.readyWait--;
}

// Make sure that the DOM is not already loaded
if ( !jQuery.readyWait || (wait !== true && !jQuery.isReady) ) {
// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
// 确保body元素存在,这个操作是防止IE的bug
if ( !document.body ) {
return setTimeout( jQuery.ready, 1 );
}

// dom渲染完成标志设置为true
jQuery.isReady = true;

// If a normal DOM Ready event fired, decrement, and wait if need be
if ( wait !== true && --jQuery.readyWait > 0 ) {
return;
}

// 绑定的渲染完成后的执行函数
if ( readyList ) {
// 全部执行
var fn,
i = 0,
ready = readyList;

// 重置
readyList = null;

while ( (fn = ready[ i++ ]) ) {
fn.call( document, jQuery );
}

// 触发所有ready事件
if ( jQuery.fn.trigger ) {
jQuery( document ).trigger( "ready" ).unbind( "ready" );
}
}
}
},
// 初始化readyList事件处理函数队列
// 兼容不同浏览对绑定事件的区别
bindReady: function() {
if ( readyBound ) {
return;
}

readyBound = true;

// $(document).ready()的嵌套调用时
// readyState: "uninitalized"、"loading"、"interactive"、"complete" 、"loaded"
if ( document.readyState === "complete" ) {
// 让它异步执行,使这个ready能延迟
return setTimeout( jQuery.ready, 1 );
}

// Mozilla, Opera and webkit
// 兼容事件,通过检测浏览器的功能特性,而非嗅探浏览器
if ( document.addEventListener ) {
// 使用事件回调函数
document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );

// 绑定回调到load,使之能一定执行
window.addEventListener( "load", jQuery.ready, false );

// IE
} else if ( document.attachEvent ) {
// 确保在load之前触发onreadystatechange,
// 针对iframe情况,可能有延迟
document.attachEvent("onreadystatechange", DOMContentLoaded);

// 绑定回调到一定执行能load事件
window.attachEvent( "onload", jQuery.ready );

// 如果是IE且非iframe情况下
// 持续的检查,看看文档是否已准备
var toplevel = false;

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

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

// ...
});
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是否渲染完毕
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;
}

// 执行在等待的函数
jQuery.ready();
}


将jQuery的ready方法剥离出,提取为函数ready及测试例子

<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script type="text/javascript">
(function(window, undefined) {
var readyList = [],
isReady = 0,
readyBound = false,
init,
bindReady,
readyWait = 1;
init = function(wait) { // A third-party is pushing the ready event forwards
if (wait === true) {
readyWait--;
} // Make sure that the DOM is not already loaded
if (!readyWait || (wait !== true && !isReady)) { // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
// 确保body元素存在,这个操作是防止IE的bug
if (!document.body) {
return setTimeout(init, 1);
} // dom渲染完成标志设置为true
isReady = true; // If a normal DOM Ready event fired, decrement, and wait if need be
if (wait !== true && --readyWait > 0) {
return;
} // 绑定的渲染完成后的执行函数
if (readyList) { // 全部执行
var fn, i = 0,
ready = readyList; // 重置
readyList = null;
while ((fn = ready[i++])) {
fn.call(document);
}
}
}
}; // 初始化readyList事件处理函数队列
// 兼容不同浏览对绑定事件的区别
bindReady = function() {
if (readyBound) {
return;
}
readyBound = true; // $(document).ready()的嵌套调用时
// readyState: "uninitalized"、"loading"、"interactive"、"complete" 、"loaded"
if (document.readyState === "complete") { // 让它异步执行,使这个ready能延迟
return setTimeout(init, 1);
} // Mozilla, Opera and webkit
// 兼容事件,通过检测浏览器的功能特性,而非嗅探浏览器
if (document.addEventListener) { // 使用事件回调函数
document.addEventListener("DOMContentLoaded",
function() {
document.removeEventListener("DOMContentLoaded", arguments.callee, false);
init();
},
false); // 绑定回调到load,使之能一定执行
window.addEventListener("load", init, false); // IE
} else if (document.attachEvent) { // 确保在load之前触发onreadystatechange,
// 针对iframe情况,可能有延迟
document.attachEvent("onreadystatechange",
function() { // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
if (document.readyState === "complete") {
document.detachEvent("onreadystatechange", arguments.callee);
init();
}
}); // 绑定回调到一定执行能load事件
window.attachEvent("onload", init); // 如果是IE且非iframe情况下
// 持续的检查,看看文档是否已准备
var toplevel = false;
try {
toplevel = window.frameElement == null;
} catch(e) {}
(function() {
if (document.documentElement.doScroll && toplevel) {
if (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(arguments.callee, 1);
return;
} // 执行在等待的函数
init();
}
})();
}
};
window.ready = function(fn) { // 绑定上监听事件
bindReady(); // 如果dom已经渲染
if (isReady) { // 立即执行
fn.call(document); // 否则,保存到缓冲队列,等上面的监听事件触发时,再全部执行
} else if (readyList) { // 将回调增加到队列中
readyList.push(fn);
}
};
})(window);
ready(function(){
var inp = document.getElementById('inp');
console.log(inp.nodeName);
});
ready(function(){
console.log('second ready');
ready(function(){
console.log('in the second ready')
})
})
</script>
</head>
<body>
<input type="text" id="inp"/>
</body>
</html>
<script type="text/javascript">
ready(function(){
console.log('third ready');
});
</script>


平时测试简单方法

// 来源于 http://www.planabc.net/2011/05/26/domready_function/ function domReady(fn) {
// "uninitalized"、"loading"、"interactive"、"complete" 、"loaded"
/in/.test(document.readyState) ? window.setTimeout(function() { domReady(fn); }, 10) : fn();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: