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

来源:https://www.cnblogs.com/greatdesert/archive/2019/11/01/11720567.html
-Advertisement-
Play Games

ready事件是當DOM文檔樹載入完成後執行一個函數(不包含圖片,css等),因此它的觸發要早於load事件。用法: $(document).ready(fun) ;fun是一個函數,這樣當DOM樹載入完畢後就會執行該匿名函數了 ready有一個簡寫,可以直接傳入$(fun)即可,這是因為在jQue ...


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

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

ready有一個簡寫,可以直接傳入$(fun)即可,這是因為在jQuey內部也定義了一個$(document)的jQuery對象,和我們在上面的寫法是一樣的

ready事件和window的onload區別:

  • ready事件  ;等dom樹載完畢後就可以執行
  • onload事件   ;等網頁中所有的資源載入完畢後(包括圖片,flash,音頻,視頻)才能執行   

onload事件還可以綁定在某個圖片上面,舉個例子:

<!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事件,載入後輸出如下:

 可以看到首先是ready事件的觸發,然後是圖片的onload事件,最後是window的onload事件的觸發,此時所有資源都已經載入完了

 

源碼分析


 jquery的ready事件就是在document上綁定了一個DOMContentLoaded事件對象,對他進行了一下封裝,DOMContentLoaded事件的原理可以看看看這篇文章,介紹得挺詳細的:https://www.cnblogs.com/caizhenbo/p/6679478.html

jQuery的ready事件是基於函數列表實現的,函數列表可以看這個連接:https://www.cnblogs.com/greatdesert/p/11433365.html

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

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()方法去觸發回掉函數列表裡的每個函數。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 範式是具有最小冗餘的表結構。 三範式具體如下: 1.第一範式(1NF):欄位都是不可再分的; 第一範式的目標是確保每列的原子性:如果每列都是不可再分的最小數據單元,則滿足第一範式(1NF); 2.第二範式(2NF):每個表只描述一件事情; 首先滿足第一範式,並且表中非主鍵屬性必須完全要依賴於主鍵屬性 ...
  • 前置操作 創建maven工程,修改pom.xml文件: 在resources添加一個file:log4j.properties: API操作 HDFS的命令和linux極其相似,可以類比記憶,在這裡列出一些java api操作: I/O流操作 上面的API操作 HDFS系統都是框架封裝好的,如果我們 ...
  • ORA-12154:tns:could not resolve the connect identifier specified ...
  • [TOC] 單表操作 分組 group by 分組指的是:將所有記錄按照某個相同欄位進行歸類,比如針對員工信息表的職位分組,或者按照性別進行分組 用法 having having是對group by後的數據進行二次篩選 order by 升序 降序 limit 限制查詢記錄的個數 offset 表示 ...
  • elasticsearch外網訪問9200埠失敗,bootstrap checks failed,the default discovery settings are unsuitable for production use; at least one of [discovery.seed_ho ...
  • 什麼時候調用imageRectForContentRect,titleRectForContentRect,contentRectForBounds,imageRectForContentRect,也是調用時機。首先梳理清楚幾個佈局順序: ...
  • 第三部分:iOS開發底層原理 1、Objective C對象模型 1.1 isa指針 NSObject.h部分代碼: objc.h部分代碼: 每個對象都有一個名為isa的指針,指向該對象的類 isa指針指向流程圖如下: 如果把類看成一個C語言的結構體(struct),isa指針就是這個結構體的第一個 ...
  • 解構革命的演變 背景 2013年中期,RSS世界遭受了沉重打擊。谷歌宣佈,他們(*的*)RSS訂閱服務,[谷歌閱讀器],是被關閉了。有了它,數以百萬計的聲音突然驚恐地大叫,並突然保持沉默。 使用量下降是關閉的主要原因,儘管來自[Google Reader]用戶的巨大反應表明,該服務仍在吸引大量用戶。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...