JS教程: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
function IEContentLoaded (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);
}

怿飞提示:

  1. 如果脚本是动态注入到页面上,则原生的 DOMContentReady 事件是不会被触发(即:IE 除外)。
  2. 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();
       
    }
    });

扩展阅读:


« 
» 
快速导航

Copyright © 2016 phpStudy | 豫ICP备2021030365号-3