欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

跨浏览器的DOMContentLoaded事件

程序员文章站 2022-06-09 18:26:31
...

原文地址: 跨浏览器的DOMContentLoaded事件

DOMContentLoaded事件是在DOM已经准备好,而其他资源可能还未下载完毕时触发的。我们可以藉此尽快的给各种交互用的dom绑定交互事件。无奈IE678不支持此事件,需要模拟。能Google到许多相关资源,比如主流框架中DOMContentLoaded事件的实现

目前,在IE中模拟DOMContentLoaded事件有3种方法。

document.attachEvent("onreadystatechange", function(){
    if ( document.readyState === "complete" ) {
        document.detachEvent( "onreadystatechange", arguments.callee );
        //这里触发DOMContentLoaded事件
    }
});
//IE中 script元素的defer属性指示浏览器在dom ready之后再加载该script
document.write("<"+"script id=__onDOMContentLoaded defer src=//:><\/script>");
document.getElementById("__onDOMContentLoaded").onreadystatechange = function() {
    if (this.readyState == "complete") {
        this.onreadystatechange = null;
        //这里触发DOMContentLoaded事件
    }
};

(function(){
    try{
        //doScroll方法只有在dom ready之后可以调用,否则会抛异常
        document.documentElement.doScroll('left');
    }catch(e){
	window.setTimeout( arguments.callee, 0 );
	return;
    }
    //这里触发DOMContentLoaded事件
})();

在3种模拟DOMContentLoaded的方法中,检测document.readyState状态的方法和检测defer的script的方法会因为页面中包含iframe而无法及时触发(要等到iframe加载完毕readyState的值才会变为complete),而调用doScroll的方法可以避免此问题。但是调用doScroll方法在脚本本身运行于iframe中时会在iframe的dom准备好之前就可以使用,我想可能是因为iframe的包含文档已经处于dom ready状态,所以导致doScroll方法可用了。

看了下jQuery-1.4.3和1.6.2的代码,选择了检测document的readyState和调用doScroll方法的组合来模拟DOMContentLoaded事件,基于上述的原因,该模拟可在页面包含iframe情况下检测doScroll方法的调用来模拟DOMContentLoaded,在包含于iframe中时检测document的readyState来模拟DOMContentLoaded。通常很少有在iframe中还有iframe的情况出现吧。jQuery源码中跨浏览器的DOMContentLoaded事件的代码可以简化为以下代码:

(function( window ){

    var readyBound = false,

        readyList = [];

	var LIZ = {
		isReady : false,

		/**
		* 注册DOM ready时的处理函数
		*/
		onReady: function ( fn ){
			LIZ.bindReady();

			// 如果此时DOM已经准备好 就直接调用注册的fn
			if ( LIZ.isReady ) {
				fn.call( document );

			// 否则就把fn推入readyList
			} else if ( readyList ) {
				readyList.push( fn );
			}
		},

		/**
		* 触发DOM ready的函数
		*/
		ready: function (){
			var fn,
				i = 0,
				ready;

			//如果DOMContentLoaded还没有触发过
			if( !LIZ.isReady ){
				LIZ.isReady = true;

				if( readyList ){

					//为了减少变量查询带来的性能损耗,将readyList赋值给本地变量
					ready = readyList;

					//释放readyList引用
					readyList = null;

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

		/**
		* 绑定DOM ready事件监听的函数
		*/
		bindReady: function (){
			if ( readyBound ) {
				return;
			}
			readyBound = true; 

			//如果在绑定ready的时候DOM已经准备好了,就异步调用ready函数
			if ( document.readyState === "complete" ) {
				return setTimeout( LIZ.ready, 0 );
			}

			if( document.addEventListener ){

				document.addEventListener( "DOMContentLoaded", function(){
					document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
					LIZ.ready();
				}, false );

				window.addEventListener( "load", LIZ.ready, false );

			} else if( document.attachEvent ){

				//如果是IE,先监听document的onreadystatechange事件
				//当document.readyState为complete时调用ready函数
				//但是当文档中还包含iframe时,readyState要等到iframe加载完毕才会改变状态为complete
				//所以用下面的doScroll方法来检测DOM是否加载完毕
				document.attachEvent("onreadystatechange", function(){
					if ( document.readyState === "complete" ) {
						document.detachEvent( "onreadystatechange", arguments.callee );
						LIZ.ready();
					}
				});
				window.attachEvent( "onload", LIZ.ready );

				var isToplevel = false;

				try {
					//判断文档是否处于最顶层
					isToplevel = window.frameElement == null;
				} catch(e) {}

				//如果文档处于iframe中,调用doScroll方法成功时并不代表DOM加载完毕
				//所以用isToplevel标识是否使用doScroll来触发ready函数
				if ( document.documentElement.doScroll && isToplevel ) {
					(function(){
						try{
							document.documentElement.doScroll('left');
						}catch(e){
							window.setTimeout( arguments.callee, 0 );
							return;
						}
						LIZ.ready();
					})();
				}
			}
		}
	};

	window.LIZ = LIZ;
})(window);
 

jQuery源码中对DOMContentLoaded事件的模拟还考虑了js异步加载的解决方案,比如异步加载jQuery的一些插件时,也要保证其代码中绑定的ready事件可以执行。为此jQuery使用一个readyWait属性来记录异步加载代码的数量,可以通过对该属性的操作来延迟DOM ready事件的触发。jQuery1.4版本中就有该属性及相关代码的存在,但是直到jQuery1.6才出现操作该属性的公开的jQuery.holdReady方法。

关于异步加载还要研究研究,还有模块化开发,最近正在看seajs的源码,看完了再写篇读后感-。-

 

本博客文章由LiZn创作或分享,以创作公用CC 姓名标示-非商业性-相同方式分享 3.0 Unported 授权条款共享。 
转载请注明 转自: 跨浏览器的DOMContentLoaded事件 
希望本文能够对你有所帮助,欢迎留言讨论,如果你喜欢本站文章,可以使用该 RSS订阅地址来订阅本站。