jQuery中的domReady分析

  我们都知道javascript中的window.onload方法的不足:必须等到所有图片和富文本媒体加载完后才能执行,影响用户体验。更好的做法是等到dom加载完即执行相应回调。类似jQuery中domReady方法应运而生。

  在jQuery中用这个方法非常简便:$(function(){}),这个其实是$(document).ready(function(){})的简便写法,见到源码:

rootjQuery = jQuery(document);
if ( jQuery.isFunction( selector ) ) {
  return rootjQuery.ready( selector );
}

  在简单的背后,是jQuery进行了封装,屏蔽了很多兼容性问题。我们来看看原生的domReady事件,了解下有那些坑。

  1,w3c的DOMContentLoaded,想说爱你不容易。

  本来是个好方法,奈何对于IE6,7,8,只能呵呵了。

  2,setTimeout

  setTimeout设置的函数, 会在readyState为complete时触发, 但是触发时间点是在图片资源加载完毕后.

  3,readyState

  readyState为interactive时, DOM结构并没有稳定, 此时依然会有脚本修改DOM元素, readyState为complete时, 图片已经加载完毕

  4,外部script(通过设置script的defer属性实现)

  外部script:如果将此script放在页面上方, 则无法稳定触发. 并且触发时DOM结构依然可能发生变化.

  5,doScroll(微软的文档只出doScroll必须在DOM主文档准备完毕时才可以正常触发. 所以通过doScroll判断DOM是否准备完毕)

  doScroll通过时readyState可能为interactive, 也可能为complete. 但是一定会在DOM结构稳定后, 图片加载完毕前执行

  方法很多,比较杂乱。jQuery的方法还是比较可取的,监听各种触发条件。宁可错杀一千,绝不放过一个。

  来看看源码的实现:

ready: function( fn ) {
    // Add the callback
    jQuery.ready.promise().done( fn );
    return this;
},

jQuery.ready.promise是个异步的Deffered对象,完成后触发回调fn。

Deferred对象什么时候完成呢?最好的结果当然是dom加载完后马上触发。这个时候就是各种监听方法。

jQuery.ready.promise = function( obj ) {
    if ( !readyList ) {
     readyList = jQuery.Deferred();
        // Catch cases where $(document).ready() is called after the browser event has already occurred.
        // we once tried to use readyState "interactive" here, but it caused issues like the one
        // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
        if ( document.readyState === "complete" ) {
            // Handle it asynchronously to allow scripts the opportunity to delay ready
            setTimeout( jQuery.ready );

        // Standards-based browsers support DOMContentLoaded
        } else if ( document.addEventListener ) {
            // Use the handy event callback
            document.addEventListener( "DOMContentLoaded", completed, false );

            // A fallback to window.onload, that will always work
            window.addEventListener( "load", completed, false );

        // If IE event model is used
        } else {
            // Ensure firing before onload, maybe late but safe also for iframes
            document.attachEvent( "onreadystatechange", completed );

            // A fallback to window.onload, that will always work
            window.attachEvent( "onload", completed );

            // If IE and not a frame
            // continually check to see if the document is ready
            var top = false;

            try {
                top = window.frameElement == null && document.documentElement;
            } catch(e) {}
        
            if ( top && top.doScroll ) {
                (function doScrollCheck() {
                    if ( !jQuery.isReady ) {

                        try {
                            // Use the trick by Diego Perini
                            // http://javascript.nwbox.com/IEContentLoaded/
                            top.doScroll("left");
                        } catch(e) {
                            return setTimeout( doScrollCheck, 50 );
                        }

                        // detach all dom ready events
                        detach();

                        // and execute any waiting functions
                        jQuery.ready();
                    }
                })();
            }
        }
    }
    return readyList.promise( obj );
};

多少判断,多少绑定......首先是判断document.readyState。如果是等于complete,立即执行回到

document.readyState === "complete"

根据浏览器的不同,分别绑定:

标准浏览器绑定:DOMContentLoaded,绑定load

document.addEventListener("DOMContentLoaded", completed, false)

window.addEventListener("load", completed, false)

IE浏览器下

document.attachEvent("onReadyStateChange", completed)

window.attachEvent("load", completed)

根据top.doScroll("left")判断是否加载完,加载完后直接触发:jQuery.ready();

再来看看completed方法

completed = function( event ) {

    // readyState === "complete" is good enough for us to call the dom ready in oldIE
    if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) {
        detach();
        jQuery.ready();
    }
},

还是触发jQuery.ready。最后来看看jQuery.ready

ready: function( wait ) {

    // Abort if there are pending holds or we‘re already ready
    if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
        return;
    }

    // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
    if ( !document.body ) {
        return setTimeout( jQuery.ready );
    }

    // Remember that the DOM is ready
    jQuery.isReady = true;

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

    // If there are functions bound, to execute
    readyList.resolveWith( document, [ jQuery ] );

    // Trigger any bound ready events
    if ( jQuery.fn.trigger ) {
        jQuery( document ).trigger("ready").off("ready");
    }
},

最后还是要触发回调:readyList.resolveWith( document, [ jQuery ] );

这个就是jQuery中domReady的实现。还是比较完美的。

优点是:加入了异步回调之后结构非常优美,想在哪里触发就在哪里触发。而且几乎全兼容。

缺点就是监听方法太多,对兼容性要求不高,不用这么费性能的方法。

 

 

 

 

 参考文档:http://www.cnblogs.com/zhangziqiu/archive/2011/06/27/DOMReady.html

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。