DOM Ready探究

        文档对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可扩展标志语言的标准编程接口。在网页上,组织页面(或文档)的对象被组织在一个树形结构中,用来表示文档中对象的标准模型就称为DOM。

        我们常说的DOM分为DOM 1,DOM 2,DOM 3。DOM0实际上是不存在的,而它实际上是在IE 4时代出现的DHTML。再具体来说,DOM 1包括DOM Core(核心)、DOM HTML(针对HTML)。DOM 2包括DOM Views(视图)、DOM Event(事件)、DOM Style(基于CSS的样式)、DOM Traversal and Range(遍历和范围)。DOM 3包括 DOM Load and Save(模块加载和存储)、DOM Validation(验证)。

        首先,我们举一个例子:


        在这段代码中,我们通过获取id为content的h1标签来给它添加了文字的颜色,我们来看看效果:


        效果并没有实现,而且还有报错:


        这是为什么呢?首先我们进入了理解的盲区,我们错把HTML中的元素理解成为了DOM节点,而两者是有本质区别的。

        在此我们先来说说浏览器渲染引擎的基本流程:

                一,解析HTML,构建DOM树;————构建DOM节点

                二,构建渲染树;——————————解析样式信息

                三,布局渲染树;——————————布局DOM节点

                四,绘制渲染树;——————————绘制DOM节点

        指得一提的是在渲染的过程中外部资源始终都在加载。

        这里找到一张webkit引擎的主要渲染流程图:

                

        其实说这些就是为了便于理解为什么在上边的demo中会报错。DOM树的建立是在HTML文档加载之前,所以根本获取不到节点。


        那么重点终于来了。我们如何解决这个问题?

        一,把JS代码写在HTML文档最后。

              好吧,第一种方法永远是最傻的。道理很简单,在较大的项目中需要多个JS代码植入,都写在下面岂不乱哉。

        二,setTimeout()

              这个很好理解,就是强行加入一个人为设定的时间。显然,这样做不合理且效率低。

        三,使用window.onload

              window.onload指的就是在HTML文档加载完之后再触发事件,这样就会相对舒服的避免了找不到对象的问题。但是,我们多思考一些,文档加载完?在前文中提到了外部资源始终都在加载,如果网速不给力、外部资源过多那很有可能我们在window.onload中写的所有所有所有代码都不会生效。这个问题很严重,于是第三种方法应运而生。

        四,DOM Ready

             英雄总是最后一个登场~  首先上代码

<script>
		function myReady(fn){		//fn就是以后的回调函数

			// 对于现代浏览器,对DOMContentLoaded事件的处理方式采用的是标准的事件绑定方式
			if(document.addEventListener){
				document.addEventListener("DOMContentLoaded", fn, false);		//通过绑定事件来侦测DOMReady,冒泡阶段捕获
			}else{
				IEContentLoaded(fn);
			}

			// IE模拟DOMCOntentLoaded
			function IEContentLoaded(fn){
				var d = window.document;
				var done = false;

				// 定义保证只执行一次用户回调的init()函数
				var init = function(){
					if(!done){
						done = true;
						fn();
					}
				};

				//侦测DOMReady并立即调用回掉函数方法
				(function(){
					try{
						// DOM 树未创建前调用doScroll会抛出错误
						d.documentElement.doScroll("left");		//如果DOM树没创建完就会抛出错误
					}
					catch(e){
						//延迟再试一次
						setTimeout(arguments.callee, 50);
						return;
					}
					// 没有错误就表示DOM树创建完毕,然后立马执行回掉函数
					init();
				})();

				// 监听document的加载状态
				d.onreadystatechange = function(){
					// 如果用户实在domReady之后绑定的函数,就立即执行
					if(d.readyState == "complete"){
						d.onreadystatechange = null;	//清除掉
						init();
					}
				}
			}
		}

        注释写的很清楚,主要再来说一下思路:使用DOM Ready的关键是DOMContentLoaded事件。这个事件是用来判断DOM树是否加载完,就是解析HTML的第一步,如果加载完成就立刻绑定事件到元素。

       做好完美从来都不容易。定义了IEContentLoad()函数就是为了解决IE无法识别该事件的问题。思路是这样的:首先,能够实现效果的核心是利用IE中的documntElement.doScroll("left")方法来判断DOM树是否创建完毕。

第一步,定义init()回调函数,保证在DOM Ready后只执行一次。

第二步,定义了onreadystatechange()函数来监听document的加载状态(注:代码中将document存在了变量d中)。通过if语句判断document是否加载完成。这里利用readState方法,判断其值是否为complete。这时候将onreadystatechange事件清除掉,因为每当 readyState 改变时,就会触发 onreadystatechange 事件,这里涉及到Ajax的相关知识。之后再执行init()完成document的加载判定。

最后一步,定义一个自调函数来解决如果document加载的状态未就绪如何侦测到DOM Ready并立即调用回调函数。这里就利用到了documntElement.doScroll("left")方法在DOM树没加载完时会抛出错误的特点(注:left是随便传的),配合try...catch语句再实现延迟调用。延迟调用这里利用到了arguments.callee属性来使这个匿名函数实现调用。最后用return;实现递归。直到没有错误时就确保了了DOM树创立完毕并立刻执行回调。

        OK~解释了这么多我们来看看效果。依然是最初的那个demo,我们来试一试:

        使用我们的函数:


        效果:

         

        Bingo~达到了我们的效果~

       

        我们写一个测试实施效果~:

HTML代码:

<body>
	<div id="show"></div>
	<div>
		<img src="1.jpg" alt="">
		<img src="2.jpg" alt="">
		<img src="3.jpg" alt="">
		<img src="4.jpg" alt="">
		<img src="5.jpg" alt="">
		<img src="6.jpg" alt="">
	</div>
</body>


JS代码:

var time1 = null;
var time2 = null;

	myReady(function(){
		var msgBox = document.getElementById("show");
		var imgs = document.getElementsByTagName("img");
		msgBox.innerHTML += "DOM加载完成</br>";
		time1 = new Date().getTime();
		msgBox.innerHTML += "时间:" + time1 + "</br>";
	});
	window.onload = function(){
		var msgBox = document.getElementById("show");
		var imgs = document.getElementsByTagName("img");
		
		msgBox.innerHTML += "window.onload加载完成</br>";
		time2 = new Date().getTime();
		msgBox.innerHTML += "时间:" + time2 + "</br>";
		msgBox.innerHTML += "DOM Ready比window.onload快" + (time2 - time1) + "毫秒</br>";
	}

        显而易见的结果~

            


        最后,附上找到的jquery的DOM Ready方法:

function jQuery(a,c) {
        if ( a && a.constructor == Function && jQuery.fn.ready ){
            return jQuery(document).ready(a);
        }
    }
    jQuery.fn.extend({
        ready: function(f) {
            if ( jQuery.isReady )
                f.apply( document );
            else {
                jQuery.readyList.push( f );
            }        
            return this;
        }
    });
    jQuery.extend({
        isReady: false,
        readyList: [],
        ready: function() {
            if ( !jQuery.isReady ) {
                jQuery.isReady = true;
                if ( jQuery.readyList ) {
                    for ( var i = 0; i < jQuery.readyList.length; i++ )
                        jQuery.readyList[i].apply( document );
                    jQuery.readyList = null;
                }
            }
        }
    });
    new function(){
        if ( jQuery.browser.mozilla || jQuery.browser.opera ) {
            document.addEventListener( "DOMContentLoaded", jQuery.ready, false );
        } else if ( jQuery.browser.msie ) {
            document.write("<scr" + "ipt id=__ie_init defer=true " + 
                "src=//:><\/script>");
            var script = document.getElementById("__ie_init");
            script.onreadystatechange = function() {
                if ( this.readyState == "complete" )
                    jQuery.ready();
            };
            script = null;
        } else if ( jQuery.browser.safari ) {
            jQuery.safariTimer = setInterval(function(){
                if ( document.readyState == "loaded" || 
                    document.readyState == "complete" ) {
                    clearInterval( jQuery.safariTimer );
                    jQuery.safariTimer = null;
                    jQuery.ready();
                }
            }, 10);
        } 
        jQuery.event.add( window, "load", jQuery.ready );    
    }


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值