深入分析jQuery的ready函數(shù)是如何工作的(工作原理)
本文深入分析jQuery的ready函數(shù)是如何工作的。分享給大家供大家參考,具體如下:
jQuery 是一個(gè)偉大的腳本庫(kù),由 John Resig 在 2006年1月的BarCamp NYC上釋出第一個(gè)版本。你可以在 http://jquery.com/ 下載到最新版本,截止本文發(fā)布為止已更新到j(luò)Query 2.1.4版。這里以jQuery1.8.3為例分析。
學(xué)習(xí) jQuery 有許多途徑,我們今天從 jQuery 的 ready 函數(shù)開(kāi)始。本例中的代碼都來(lái)自于 jQuery 腳本庫(kù)。
如果你使用過(guò) jQuery , 就必然使用過(guò) ready 函數(shù),它用來(lái)注冊(cè)當(dāng)頁(yè)面準(zhǔn)備好之后可以執(zhí)行的函數(shù)。
問(wèn)題來(lái)啦,我們的頁(yè)面什么時(shí)候準(zhǔn)備好了呢?
1. onload 事件
最基本的處理方式就是頁(yè)面的 onload 事件,我們?cè)谔幚磉@個(gè)事件的時(shí)候,可以有多種方式,即可以通過(guò) HTML 方式,直接寫在 body 元素的開(kāi)始標(biāo)記中,也可以使用事件注冊(cè)的方式來(lái)使用,這又可以分為 DOM0 方式和 DOM2 方式。再考慮到瀏覽器的兼容性,使用 DOM2 方式寫出來(lái),如下所示。
if (document.addEventListener) { // A fallback to window.onload, that will always work window.addEventListener("load", jQuery.ready, false); // If IE event model is used } else { // A fallback to window.onload, that will always work window.attachEvent("onload", jQuery.ready); }
2. DOMContentLoaded 事件
不過(guò) onload 事件要等到所有頁(yè)面元素加載完成才會(huì)觸發(fā), 包括頁(yè)面上的圖片等等。如果網(wǎng)頁(yè)上有大量的圖片,效果可想而知,用戶可能在沒(méi)有看到圖片的時(shí)候,就已經(jīng)開(kāi)始操作頁(yè)面了,而這時(shí)我們的頁(yè)面還沒(méi)有初始化,事件還沒(méi)有注冊(cè)上,這豈不是太晚了!
除了大家熟知的 onload 事件之外, 與 DOM 中的 onload 事件相近的,我們還有 DOMContentLoaded 事件可以考慮, 基于標(biāo)準(zhǔn)的瀏覽器支持這個(gè)事件, 當(dāng)所有 DOM 解析完以后會(huì)觸發(fā)這個(gè)事件。
這樣,對(duì)于基于標(biāo)準(zhǔn)的瀏覽器來(lái)說(shuō),我們還可以注冊(cè)這個(gè)事件的處理。這樣,我們可能更早地捕獲到加載完成的事件。
if (document.addEventListener) { // Use the handy event callback document.addEventListener("DOMContentLoaded", DOMContentLoaded, false); // A fallback to window.onload, that will always work window.addEventListener("load", jQuery.ready, false); }
3. onreadystatechange 事件
不標(biāo)準(zhǔn)的瀏覽器怎么辦呢?
如果瀏覽器存在 document.onreadystatechange 事件,當(dāng)該事件觸發(fā)時(shí),如果 document.readyState=complete 的時(shí)候,可視為 DOM 樹(shù)已經(jīng)載入。
不過(guò),這個(gè)事件不太可靠,比如當(dāng)頁(yè)面中存在圖片的時(shí)候,可能反而在 onload 事件之后才能觸發(fā),換言之,它只能正確地執(zhí)行于頁(yè)面不包含二進(jìn)制資源或非常少或者被緩存時(shí)作為一個(gè)備選吧。
if (document.addEventListener) { // Use the handy event callback document.addEventListener("DOMContentLoaded", DOMContentLoaded, false); // A fallback to window.onload, that will always work window.addEventListener("load", jQuery.ready, false); // If IE event model is used } else { // 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); }
DOMContentLoaded 函數(shù)在做什么呢?最終還是要調(diào)用 jQuery.ready 函數(shù)。
DOMContentLoaded = function() { if ( document.addEventListener ) { document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); jQuery.ready(); } else if ( document.readyState === "complete" ) { // we're here because readyState === "complete" in oldIE // which is good enough for us to call the dom ready! document.detachEvent( "onreadystatechange", DOMContentLoaded ); jQuery.ready(); } }
4. doScroll 檢測(cè)法
MSDN 關(guān)于 JScript 的一個(gè)方法有段不起眼的話,當(dāng)頁(yè)面 DOM 未加載完成時(shí),調(diào)用 doScroll 方法時(shí),會(huì)產(chǎn)生異常。那么我們反過(guò)來(lái)用,如果不異常,那么就是頁(yè)面DOM加載完畢了!
Diego Perini 在 2007 年的時(shí)候,報(bào)告了一種檢測(cè) IE 是否加載完成的方式,使用 doScroll 方法調(diào)用。詳細(xì)的說(shuō)明見(jiàn)這里。
原理是對(duì)于 IE 在非 iframe 內(nèi)時(shí),只有不斷地通過(guò)能否執(zhí)行 doScroll 判斷 DOM 是否加載完畢。在本例中每間隔 50 毫秒嘗試去執(zhí)行 doScroll,注意,由于頁(yè)面沒(méi)有加載完成的時(shí)候,調(diào)用 doScroll 會(huì)導(dǎo)致異常,所以使用了 try -catch 來(lái)捕獲異常。
(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); } // and execute any waiting functions jQuery.ready(); } })();
5. document.readyState 狀態(tài)
如果我們注冊(cè) ready 函數(shù)的時(shí)間點(diǎn)太晚了,頁(yè)面已經(jīng)加載完成之后,我們才注冊(cè)自己的 ready 函數(shù),那就用不著上面的層層檢查了,直接看看當(dāng)前頁(yè)面的 readyState 就可以了,如果已經(jīng)是 complete ,那就可以直接執(zhí)行我們準(zhǔn)備注冊(cè)的 ready 函數(shù)了。不過(guò) ChrisS 報(bào)告了一個(gè)很特別的錯(cuò)誤情況,我們需要延遲一下執(zhí)行。
setTimeout 經(jīng)常被用來(lái)做網(wǎng)頁(yè)上的定時(shí)器,允許為它指定一個(gè)毫秒數(shù)作為間隔執(zhí)行的時(shí)間。當(dāng)被啟動(dòng)的程序需要在非常短的時(shí)間內(nèi)運(yùn)行,我們就會(huì)給她指定一個(gè)很小的時(shí)間數(shù),或者需要馬上執(zhí)行的話,我們甚至把這個(gè)毫秒數(shù)設(shè)置為0,但事實(shí)上,setTimeout有一個(gè)最小執(zhí)行時(shí)間,當(dāng)指定的時(shí)間小于該時(shí)間時(shí),瀏覽器會(huì)用最小允許的時(shí)間作為setTimeout的時(shí)間間隔,也就是說(shuō)即使我們把setTimeout的毫秒數(shù)設(shè)置為0,被調(diào)用的程序也沒(méi)有馬上啟動(dòng)。
這個(gè)最小的時(shí)間間隔是多少呢?這和瀏覽器及操作系統(tǒng)有關(guān)。在John Resig的新書《Javascript忍者的秘密》一書中提到
Browsers all have a 10ms minimum delay on OSX and a(approximately) 15ms delay on Windows.(在蘋果機(jī)上的最小時(shí)間間隔是10毫秒,在Windows系統(tǒng)上的最小時(shí)間間隔大約是15毫秒),另外,MDC中關(guān)于setTimeout的介紹中也提到,F(xiàn)irefox中定義的最小時(shí)間間隔(DOM_MIN_TIMEOUT_VALUE)是10毫秒,HTML5定義的最小時(shí)間間隔是4毫秒。既然規(guī)范都是這樣寫的,那看來(lái)使用setTimeout是沒(méi)辦法再把這個(gè)最小時(shí)間間隔縮短了。
這樣,通過(guò)設(shè)置為 1, 我們可以讓程序在瀏覽器支持的最小時(shí)間間隔之后執(zhí)行了。
// 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") { // 延遲 1 毫秒之后,執(zhí)行 ready 函數(shù) setTimeout(jQuery.ready, 1); }
6. 完整的代碼
在 jQuery 中完整的代碼如下所示。位于 jQuery 1.8.3 源代碼的 #842 行。
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, 1 ); // Standards-based browsers support DOMContentLoaded } else if ( document.addEventListener ) { // Use the handy event callback document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); // A fallback to window.onload, that will always work window.addEventListener( "load", jQuery.ready, false ); // If IE event model is used } else { // 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 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 ); } // and execute any waiting functions jQuery.ready(); } })(); } } } return readyList.promise( obj ); };
那么,又是誰(shuí)來(lái)調(diào)用呢?當(dāng)然是需要的時(shí)候,在我們調(diào)用 ready 函數(shù)的時(shí)候,才需要注冊(cè)這些判斷頁(yè)面是否完全加載的處理,這段代碼在 1.8.3 中位于代碼的 #244 行,如下所示:
ready: function( fn ) { // Add the callback jQuery.ready.promise().done( fn ); return this; }
在頁(yè)面上引用 jQuery 腳本庫(kù)之后,執(zhí)行了 jQuery 的初始化函數(shù),初始化函數(shù)中創(chuàng)建了 ready 函數(shù)。我們?cè)谕ㄟ^(guò) ready 函數(shù)注冊(cè)事件處理之前,jQuery 完成了頁(yè)面檢測(cè)代碼的注冊(cè)。這樣。當(dāng)頁(yè)面完全加載之后,我們注冊(cè)的函數(shù)就被調(diào)用了。
補(bǔ)充:jquery ready 簡(jiǎn)寫模式
Jquery ready 函數(shù):
$(document).ready(function(){ alert(' i am ready'); } );
可簡(jiǎn)寫為:
$(function(){ alert("i am in"); } );
希望本文所述對(duì)大家jQuery程序設(shè)計(jì)有所幫助。
- jquery事件的ready()方法使用詳解
- JavaScript的jQuery庫(kù)中ready方法的學(xué)習(xí)教程
- jquery $(document).ready()和window.onload的區(qū)別淺析
- jQuery中ready事件用法實(shí)例
- jQuery的ready方法詳解
- 提取jquery的ready()方法單獨(dú)使用示例
- jquery中的$(document).ready()使用小結(jié)
- Jquery中"$(document).ready(function(){ })"函數(shù)的使用詳解
- JQuery的ready函數(shù)與JS的onload的區(qū)別詳解
- jquery ready(fn)事件使用介紹
相關(guān)文章
jquery 設(shè)置元素相對(duì)于另一個(gè)元素的top值(實(shí)例代碼)
在jquery中offset().top是相對(duì)于body來(lái)說(shuō)的,另外在設(shè)置top值的時(shí)候要找到與該元素最近的有相對(duì)值的元素2013-11-11jQuery簡(jiǎn)單實(shí)現(xiàn)MD5加密的方法
這篇文章主要介紹了jQuery簡(jiǎn)單實(shí)現(xiàn)MD5加密的方法,基于jquery.md5.js插件實(shí)現(xiàn)md5加密功能,非常簡(jiǎn)單實(shí)用,需要的朋友可以參考下2017-03-03jQuery Ajax 全局調(diào)用封裝實(shí)例代碼詳解
這篇文章主要介紹了jQuery Ajax 全局調(diào)用封裝實(shí)例代碼詳解的相關(guān)資料,非常不錯(cuò)具有參考借鑒價(jià)值,需要的朋友可以參考下2016-06-06基于jquery的設(shè)置頁(yè)面文本框 只能輸入數(shù)字的實(shí)現(xiàn)代碼
之前寫過(guò)的方法有缺陷,可以輸入空格?,F(xiàn)在將空格也屏蔽了。就是在之前的代碼里加入了過(guò)濾空格的功能。2011-04-04jQuery 類twitter的文本字?jǐn)?shù)限制帶提示效果插件
基于jquery的仿twitter的文本字?jǐn)?shù)限制帶提示效果插件,這里提示比較好,不是簡(jiǎn)單的限制,給用戶更好的體驗(yàn)。2010-04-04基于jQuery實(shí)現(xiàn)仿微博發(fā)布框字?jǐn)?shù)提示
這篇文章主要為大家詳細(xì)介紹了基于jQuery實(shí)現(xiàn)仿微博發(fā)布框字?jǐn)?shù)提示的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-07-07jquery+css3打造一款ajax分頁(yè)插件(自寫)
這篇文章主要介紹了自己寫的一款ajax分頁(yè)插件,用jquery+css3打造支持IE6+,但沒(méi)有動(dòng)畫效果,需要的朋友可以參考下2014-06-06jquery實(shí)現(xiàn)二級(jí)導(dǎo)航下拉菜單效果實(shí)例
這篇文章主要介紹了jquery二級(jí)導(dǎo)航下拉菜單,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05