jQuery 源碼分析(十八) ready事件詳解

ready事件是當DOM文檔樹加載完成後執行一個函數(不包含圖片,css等),所以它的觸發要早於load事件。用法:css

  • $(document).ready(fun)    ;fun是一個函數,這樣當DOM樹加載完畢後就會執行該匿名函數了

ready有一個簡寫,能夠直接傳入$(fun)便可,這是由於在jQuey內部也定義了一個$(document)的jQuery對象,和咱們在上面的寫法是同樣的html

ready事件和window的onload區別:node

  • ready事件  ;等dom樹載完畢後就能夠執行
  • onload事件   ;等網頁中全部的資源加載完畢後(包括圖片,flash,音頻,視頻)才能執行   

onload事件還能夠綁定在某個圖片上面,舉個例子:jquery

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="http://libs.baidu.com/jquery/1.11.1/jquery.min.js"></script>
</head>
<body>
    <img src="https://www.cnblogs.com/images/logo_small.gif"  alt="">
    <script>
        $(()=>console.log('DOM樹已加載完畢'))                        //ready事件
        $('img').on('load',()=>console.log('圖片已加載完畢'))        //圖片的加載事件
        $(window).on('load',()=>console.log('資源已加載完畢'))       //網頁全部資源都加載完畢後的事件        
    </script>
</body>
</html>

這裏咱們用了箭頭函數來寫,代碼很簡單了,咱們在綁定了一個ready事件,一個圖片上的onload事件和window上的onload事件,加載後輸出以下:web

 能夠看到首先是ready事件的觸發,而後是圖片的onload事件,最後是window的onload事件的觸發,此時全部資源都已經加載完了瀏覽器

 

源碼分析緩存


 jquery的ready事件就是在document上綁定了一個DOMContentLoaded事件對象,對他進行了一下封裝,DOMContentLoaded事件的原理能夠看看看這篇文章,介紹得挺詳細的:http://www.javashuo.com/article/p-csqrmndx-g.htmldom

jQuery的ready事件是基於函數列表實現的,函數列表能夠看這個鏈接:http://www.javashuo.com/article/p-vnyzfueg-v.html異步

當咱們調用$(fun)去執行一個ready事件的時候首先會執行入口模塊裏的邏輯,與ready相關的以下:async

init: function( selector, context, rootjQuery ) {
    var match, elem, ret, doc;

    // Handle $(""), $(null), or $(undefined)
    if ( !selector ) {
        return this;
    }

    // Handle $(DOMElement)
    if ( selector.nodeType ) {
        this.context = this[0] = selector;
        this.length = 1;
        return this;
    }

    // The body element only exists once, optimize finding it
    if ( selector === "body" && !context && document.body ) {
        this.context = document;
        this[0] = document.body;
        this.selector = selector;
        this.length = 1;
        return this;
    }

    // Handle HTML strings
    if ( typeof selector === "string" ) {
        /**/
    } else if ( jQuery.isFunction( selector ) ) {            //若是參數selector是函數,則認爲是綁定ready事件
        return rootjQuery.ready( selector );                    //則執行rootjQuery.ready()方法,並把selector做爲參數傳入
    }

    /**/
},

rootjQuery是jQuery內部定義的一個局部變量,是一個jQuery實例,以下:

rootjQuery = jQuery(document);                       //第917行,保存了document對象引用的jQuery實例

在入口模塊引用rootjQuery.ready()也就是執行了rootjQuery實例對象上的ready方法(該方法是定義在原型上的),以下:

jQuery.fn = jQuery.prototype = {
    ready: function( fn ) {
        // Attach the listeners
        jQuery.bindReady();                 //先執行jQuery.bindReady()綁定ready事件(實際上綁定的是DOMContentLoaded或onreadystatechange事件)

        // Add the callback
        readyList.add( fn );                //爲函數列表readyList增長一個函數

        return this;
    }
}

jQuery.bindReady()是一個靜態方法,用於綁定事件的,內部會初始化readyList爲一個jQuery.Callbacks( "once memory" )函數列表對象

而後執行readyList.add( fn )將fn函數保存到函數列表readyList裏面。

jQuery.bindReady()的實現以下:

jQuery.extend({
    bindReady: function() {                            //初始化ready事件監聽函數列表readyList,併爲document對象綁定ready事件主監聽函數DOMContentLoaded
        if ( readyList ) {
            return;
        }

        readyList = jQuery.Callbacks( "once memory" );                        //調用jQuery.Callbacks(flags)ready事件監聽函數列表readylist,同時傳入once和memory標記。

        // Catch cases where $(document).ready() is called after the
        // browser event has already occurred.
        if ( document.readyState === "complete" ) {                            //若是文檔已經就緒,則調用jQuery.ready(wait)執行ready事件監聽函數列表readyList
            // Handle it asynchronously to allow scripts the opportunity to delay ready
            return setTimeout( jQuery.ready, 1 );                                //經過setTimeout()異步執行方法jQuery.ready(wait),以容許其餘腳本延遲ready事件的觸發。
        }

        // Mozilla, Opera and webkit nightlies currently support this event
        if ( document.addEventListener ) {                                     //在IE9+及以上瀏覽器綁定DOMContentLoaded事件
            // Use the handy event callback
            document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );         //把監聽函數DOMContentLoaded綁定到document對象的DOMContentLoaded事件上

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

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

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

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

            try {
                toplevel = window.frameElement == null;
            } catch(e) {}

            if ( document.documentElement.doScroll && toplevel ) {
                doScrollCheck();
            }
        }
    },
    /**/
})

這裏咱們調用document.addEventListener在document上綁定了一個DOMContentLoaded事件,這樣當DOM樹加載完後就會執行DOMContentLoaded函數了,DOMContentLoaded函數的定義以下:

if ( document.addEventListener ) {         //若是是IE9+和其餘瀏覽器
    DOMContentLoaded = function() {
        document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );    //先移除document的DOMContentLoaded事件
        jQuery.ready();                                                                    //再調用jQuery.ready()執行ready事件監聽函數
    };

} else if ( document.attachEvent ) {
    DOMContentLoaded = function() {
        // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
        if ( document.readyState === "complete" ) {
            document.detachEvent( "onreadystatechange", DOMContentLoaded );
            jQuery.ready();
        }
    };
}

函數內首先會移除DOMContentLoaded事件,而後調用jQuery.ready()事件,這是DOM樹觸發後的事件了(咱們在jQuery.fn.ready()內執行了readyList.add( fn )增長的函數都會依次觸發),以下:

jQuery.extend({
    isReady: false,
    ready: function( wait ) {                        //實際執行的函數  觸發ready事件監聽函數列表readyList和數據緩存對象中的ready事件監聽函數。
        // Either a released hold or an DOMready/load event and not yet ready
        if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) {    //若是wait是true且jQuery.readyWait等於0 或者 wait不是true且jQuery.isReady是false 則執行 初始化jQuery.isReady爲false的
            // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
            if ( !document.body ) {
                return setTimeout( jQuery.ready, 1 );
            }

            // Remember that the DOM is ready
            jQuery.isReady = true;                                        //設置jQuery.inReady爲true,表示ready事件已就緒。

            // 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.fireWith( document, [ jQuery ] );                 //執行ready事件監聽函數readyList,上下文是document(即關鍵詞this),[jQuery]是ready事件監聽函數的參數。

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

 writer by:大沙漠 QQ:22969969

最後調用readyList.fireWith()方法去觸發回掉函數列表裏的每一個函數。

相關文章
相關標籤/搜索