今天為大家介紹的是jQuery源碼dom ready分析的相關內容,希望大家會喜歡。
一、前言
在平時開發web項目時,我們使用jquery框架時,可能經常這樣來使用(document).ready(fn),(function(){}),這樣使用的原因是在浏覽器把DOM樹渲染好之前,javascript是無法操作沒渲染好的DOM節點。
其實除了(document).ready(fn),(function(){})寫法外,還有兩種讓dom渲染完之後執行js的寫法:
$(document).on('ready', fn2)
//通過on事件綁定函數,通過trigger觸發也可以達到j
Query.ready.promise().done(fn);
//通過這種方式也可以實現,jQuery.ready.promise()返回一個deferred對象,done(fn)添加回調方法
其中具體流程圖如下(自己簡單畫了一下,有錯請大家指正)
二、源碼部分(建議看這部分是,先理解清楚deferred,promise)
①$(function(){}) =>到rootjQuery.ready(selector);
我們知道,jQuery是由new jQuery.fn.init(selector, context, rootjQuery)實例出來的,對接了兩個參數,selector,context
// 構造函數,定義一個局部變量的jQueryjQuery = function (selector, context) {
// jQuery對象實際上是init的構造函數的引用return new jQuery.fn.init(selector, context, rootjQuery);
}
當我們使用(function()),則選擇器selector參數就變成了funciton,jQuery.fn.init函數判斷selector為Funtion時,又指向了
rootjQuery.ready(selector),就是(document).ready(fn);
rootjQuery = $(document)
jQuery.fn = jquery.prototype = {
init:function(selector,context,rootjQuery){
if (jQuery.isFunction(selector)) {
// 引用非靜態成員ready方法,等價於$(document).ready(selector)return rootjQuery.ready(selector);}
②$(document).on("ready",fn) => 到jQuery(document).trigger("ready").off("ready");
要理解$(document).on("ready",fn),我們要看ready部分
//擴展方法到jquery***
jQuery.extend({
/**
記錄監聽DOMContentLoaded事件(DOM是否加載完成),加載完成設置為true(類似一個開關)
* type {Boolean} 默認為false,表示頁面未加載完。當頁面DOM加載完成,設置為true */isReady: false,
/**
需要預加載的觀察者數量
* type {Number} 觀察者數量 */readyWait: 1,
/**
DOM加載完成,執行預加載
* @param {Boolean} wait 為true表示鎖定委托人,為false表示釋放委托人*/ready: function (wait) {
if (wait === true ? --jQuery.readyWait : jQuery.isReady) {
return;
}
// 檢測body是否存在(IE的一個bug),存在繼續向下執行,不存在進入語句,執行setTimeout定時器,直到body存在if (!document.body) {
return setTimeout(jQuery.ready);
}
// 開關,記錄DOM加載完成jQuery.isReady = true;
// 檢測所有的觀察者是否執行完成if (wait !== true && --jQuery.readyWait > 0) {
return;
}
// 委托人readyList通知觀察者,開始執行回調函數,並將document作為這些函數的上下文環境
readyList.resolveWith(document, [jQuery]);
// 檢測trigger方法是否存在,觸發綁定在document上的事件,執行完成之後並解綁
// $(document).on('ready', fn2);
// $(document).ready(fn1);
//這裡的fn1會先執行,自己的ready事件綁定的fn2回調後執行if (jQuery.fn.trigger) {
jQuery(document).trigger("ready").off("ready");
})
③$(document).ready(fn)
jQuery.fn = jquery.prototype = {
if (jQuery.isFunction(selector)) {
// 引用非靜態成員ready方法return rootjQuery.ready(selector);
},
ready: function (fn) {
// Add the callback
// promise類似一種事件委托,相當於一個創建委托人(已創建則無需創建,通過readyList判斷是否已創建),在DOM加載完成之後,委托人會通知觀察者done去執行回調函數fn
//調委托函數,返回deferred對象,添加done(fn)回調函數
jQuery.ready.promise().done(fn);
return this;
}
}
最後dom ready相關部分源碼詳細如下:
// 構造函數,定義一個局部變量的jQueryjQuery = function (selector, context) {
// jQuery對象實際上是init的構造函數的引用return new jQuery.fn.init(selector, context, rootjQuery);
} // 就緒事件處理程序completed = function (event) {
// document.readyState 判斷文檔加載狀態,'complete'代表文檔已經完全加載 if (document.addEventListener || event.type === "load" || document.readyState === "complete") {
detach();
//清理方法
jQuery.ready();//執行延遲加載方法 }
} // 清理DOMContentLoaded事件處理程序,為DOM事件做好准備,觸發jQuery.ready方法detach = function () {
// 標准的W3C監聽事件if (document.addEventListener) {
//刪除DOMContentLoaded監聽事件document.removeEventListener("DOMContentLoaded", completed, false);
window.removeEventListener("load", completed, false);
} else {
// 針對IE,非標准的浏覽器
// 刪除onreadystatechange監聽事件document.detachEvent("onreadystatechange", completed);
window.detachEvent("onload", completed);
}
}; var readyList,rootjQuery=$(document);
jQuery.fn = jquery.prototype = { if (jQuery.isFunction(selector)) {
// 引用非靜態成員ready方法return rootjQuery.ready(selector);
},
ready: function (fn) {
// Add the callback
// promise類似一種事件委托,相當於一個創建委托人(已創建則無需創建,通過readyList判斷是否已創建),在DOM加載完成之後,委托人會通知觀察者done去執行回調函數fn
//調委托函數,返回deferred對象,添加done(fn)回調函數
jQuery.ready.promise().done(fn);
return this;
}}
//擴展方法到jquery****
jQuery.extend({
/**記錄監聽DOMContentLoaded事件(DOM是否加載完成),加載完成設置為true(類似一個開關)
* type {Boolean} 默認為false,表示頁面未加載完。當頁面DOM加載完成,設置為true */isReady: false,
/**
需要預加載的觀察者數量
* type {Number} 觀察者數量 */readyWait: 1,
/**
鎖定或釋放預加載委托人
* @param {Boolean} hold 為true表示鎖定預加載委托人,為false表示釋放委托人 */holdReady: function (hold) {
if (hold) {
jQuery.readyWait++;
} else {
jQuery.ready(true);
}},
/**
DOM加載完成,執行預加載
* @param {Boolean} wait 為true表示鎖定委托人,為false表示釋放委托人 */ ready: function (wait) {
// Abort if there are pending holds or we're already readyif (wait === true ? --jQuery.readyWait : jQuery.isReady) {
return;
}
// 檢測body是否存在(IE的一個bug),存在繼續向下執行,不存在進入語句,執行setTimeout定時器,直到body存在if (!document.body) {
return setTimeout(jQuery.ready);
} // 開關,記錄DOM加載完成jQuery.isReady = true;
// 檢測所有的觀察者是否執行完成if (wait !== true && --jQuery.readyWait > 0) {
return;
}
// 委托人readyList通知觀察者,開始執行回調函數,並將document作為這些函數的上下文環境
readyList.resolveWith(document, [jQuery]);
// 檢測trigger方法是否存在,觸發綁定在document上的事件,執行完成之後並解綁
// $(document).on('ready', fn2);
// $(document).ready(fn1);
//這裡的fn1會先執行,自己的ready事件綁定的fn2回調後執行if (jQuery.fn.trigger) {
jQuery(document).trigger("ready").off("ready");
}})
// 預加載委托函數jQuery.ready.promise = function (obj) {
// 判斷預加載委托人是否存在if (!readyList) {
// 創建一個預加載委托人readyList = jQuery.Deferred();
/**
W3C標准DOM浏覽器
* 0-uninitialized:XML 對象被產生,但沒有任何文件被加載。
* 1-loading:加載程序進行中,但文件尚未開始解析。
* 2-loaded:部分的文件已經加載且進行解析,但對象模型尚未生效。
* 3-interactive:僅對已加載的部分文件有效,在此情況下,對象模型是有效但只讀的。
* 4-complete:文件已完全加載,代表加載成功。 */// 檢測document內容是否加載完成,加載完成返回true,否者返回falseif (document.readyState === "