jQuery源码dom ready分析
一、前言
在平時開發web項目時,我們使用jquery框架時,可能經常這樣來使用$(document).ready(fn),$(function(){}),這樣使用的原因是在瀏覽器把DOM樹渲染好之前,javascript是無法操作沒渲染好的DOM節點。
其實除了$(document).ready(fn),$(function(){})寫法外,還有兩種讓dom渲染完之后執行js的寫法:
$(document).on('ready', fn2) //通過on事件綁定函數,通過trigger觸發也可以達到 jQuery.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 === "complete") {/*** 函數延遲0毫秒執行并不是立即執行, 而是等瀏覽器運行完掛起的事件句柄和已經更新完文檔狀態之后才* 運行這個函數.詳情見《JavaScript Definition Guide 5th Edition(JavaScript權威指南第5版)》* 函數延遲1毫秒或者為空代表立即執行*/// 立即調用jQuery.ready方法,執行預加載內容 setTimeout(jQuery.ready);// 檢測是否符合W3C標準事件模型(IE不支持)} else if (document.addEventListener) {// 監聽DOMContentLoaded事件,當DOM加載完成,觸發completed方法// DOMContentLoaded事件在DOM加載完成時觸發// completed方法,目的是先刪除監聽DOMContentLoaded事件,然后執行jQuery.ready()方法。(刪除事件,清除內存)document.addEventListener("DOMContentLoaded", completed, false);// 若失敗,給window.onload注冊一個jQuery.ready方法,并在頁面加載完成之后執行,目的是兼容低版本瀏覽器,防止出現預加載失敗window.addEventListener("load", completed, false);// IE事件模型} else {// 監聽onreadystatechange事件,當DOM加載完成,觸發DOMContentLoaded方法document.attachEvent("onreadystatechange", completed);// 同理,若失敗,兼容操作.window.attachEvent("onload", completed);// 聲明一個變量top, 目的:如果在IE下,文檔不是嵌套在框架中, 就不斷地檢測文檔是否準備就緒.var top = false;try {//檢測是否嵌套在框架中,嵌套在框架中返回false,不嵌套返回document.documentElement //返回嵌入當前window對象的元素(比如 <iframe> 或者 <object>),如果當前window對象已經是頂層窗口,則返回null,如果有框架則document.documentElement返回為null.top = window.frameElement == null && document.documentElement;} catch (e) { }// 檢測是否存在doScroll方法// IE支持doScroll,doScroll捕捉頁面垂直和水平的滾動,他在ondocumentready 事件觸發之后,onload事件觸發之前觸發if (top && top.doScroll) {(function doScrollCheck() {if (!jQuery.isReady) {try {top.doScroll("left");} catch (e) {return setTimeout(doScrollCheck, 50);}detach();// 執行jQuery.ready方法,說明頁面DOM加載完成,其實就是改變deffered對象的狀態,resolvewith完成,觸發完成回調done(fn) jQuery.ready();}})();}}}// 返回一個被限制的委托人(只能執行,不能改變的狀態的委托人)return readyList.promise(obj);};?
總結
以上是生活随笔為你收集整理的jQuery源码dom ready分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Hiberante+jpa,注解生成32
- 下一篇: IPv6协议介绍