模拟兼容性的 addDOMLoadEvent 事件
由于 window.onload
事件需要在页面所有内容(包括图片等)加载完后,才执行,但往往我们更希望在 DOM 一加载完就执行脚本。其实在现在大部分主流浏览器上(Firefox 3+,Opera 9+,Safari 3+,Chrome 2+)都提供了这一事件方法:addDOMLoadEvent
。
document.addEventListener("DOMContentLoaded", init,false);
那对于 IE 我们如何模拟 addDOMLoadEvent 事件呢?
Matthias Miller 最早提供了如下的解决方案:
// for Internet Explorer (using conditional comments)
/*@cc_on @*/
/*@if (@_win32)
document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>");
var script = document.getElementById("__ie_onload");
script.onreadystatechange = function() {
if (this.readyState == "complete") {
init(); // call the onload handler
}
};
/*@end @*/
而 Diego Perini 在其后提供了一种利用 doScroll()
方法来模拟 addDOMLoadEvent 事件的方案,且现在主流的 JavaScript 框架(JQuery、YUI等)基本都采用的这一解决方案。
原理基本如下:
当 ondocumentready 事件触发,文档( document )已经完全解析和建立。如果组件需要操作最初的文档结构,初始化代码需被安置在这之后。ondocumentready 事件告知组件,整个页面已被加载,且在 初始文档的 onload 事件触发之前立即触发。
一些方法,例如 doScroll,要求最初的文档被完全加载。如果这些方法是初始化函数的一部分,当ondocumentready 事件触发,他们将被执行。
/*
*
* 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 reference
// @fn function reference
functionIEContentLoaded(w, fn){
var d = w.document,done=false,
// only fire once
init =function(){
if(!done){
done=true;
fn();
}
};
// polling for no errors
(function(){
try{
// throws errors until after ondocumentready
d.documentElement.doScroll('left');
}catch(e){
setTimeout(arguments.callee,50);
return;
}
// no errors, fire
init();
})();
// trying to always fire before onload
d.onreadystatechange =function(){
if(d.readyState =='complete'){
d.onreadystatechange =null;
init();
}
};
}
JQuery 1.3.2 中源码实现如下:
// If IE and not an iframe
// continually check to see if the document is ready
if( document.documentElement.doScroll && window == window.top )(function(){
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( error ){
setTimeout( arguments.callee,0);
return;
}
// and execute any waiting functions
jQuery.ready();
})();
YUI 2.7.0 中源码实现如下:
if(EU.isIE){
// Process onAvailable/onContentReady items when the
// DOM is ready.
YAHOO.util.Event.onDOMReady(
YAHOO.util.Event._tryPreloadAttach,
YAHOO.util.Event,true);
var n = document.createElement('p');
EU._dri = setInterval(function(){
try{
// throws an error if doc is not ready
n.doScroll('left');
clearInterval(EU._dri);
EU._dri =null;
EU._ready();
n =null;
}catch(ex){
}
}, EU.POLL_INTERVAL);
}
另外对于版本小于 Safari 3+ 的 Safari 浏览器,John Resig 也提供了一个解决方案:
if(/WebKit/i.test(navigator.userAgent)){// sniff
var _timer = setInterval(function(){
if(/loaded|complete/.test(document.readyState)){
clearInterval(_timer);
init();// call the onload handler
}
},10);
}
怿飞提示:
- 如果脚本是动态注入到页面上,则原生的 DOMContentReady 事件是不会被触发(即:IE 除外)。
- IE 下对于在 iframe 里的使用 addDOMLoadEvent 事件,需做处理和慎用(这一点 YUI 做得不如 JQuery 细致)。
// form JQuery 1.3.2
// ensure firing before onload, maybe late but safe also for iframes
document.attachEvent("onreadystatechange",function(){
if( document.readyState ==="complete"){
document.detachEvent("onreadystatechange", arguments.callee );
jQuery.ready();
}
});
扩展阅读: