jQuery的document ready與 onload事件——你真的思考過嗎?


在進行實驗和資料查詢時,我遇到了幾個關鍵問題:

1. window.onload到底是什么加載完觸發?

2. body為什么會有onload事件?

3. 為什么是window.onload,而不是document.onload?

4. document ready到底是什么ready,DOM渲染完成?

5. jQuery怎么實現$(document).ready?

6. jQuery的ready,還能ready什么?

7. jQuery的document ready就一定比window.onload快嗎?

8. 為什么外部script文件放頁面內容后面好,是一定的嗎?

onload

load是一個事件,會在頁面包含文件資源加載完成后觸發。

支持該事件的HTML標簽:

body、frame、frameset、iframe、img、link、script。

這里有兩點我很在意的是:

1. 其他標簽是沒有該事件,所以不要胡亂用。

2. 就如我開頭提的問題之一,為什么body會有onload?(Question 2)

首先,我為什么有這個疑問,因為可以看出的是,支持該事件的其他標簽都是為了加載標簽里的src資源,而body顯然不是。

我在《JavaScript高級程序設計》找到一個合理的解釋:

一般來說,在window上面發生的任何事件都可以在body元素中通過相應的特性來指定,因為在HTML中無法訪問window元素。這樣是為了保證向后兼容的權宜之計,但所有瀏覽器都能很好地支持這種方式。

結論:為了對應上window.onload,body標簽才有onload。

支持該事件的JavaScript對象:

image、window。

這里有兩個問題我很在意:

1. window.onload到底是什么加載完觸發?(Question 1)

依資料記載,當頁面完全加載后(包括所有圖像、JavaScript文件等外部資源),就會觸發該事件。這方式跟在body上寫onload效果是一樣的。

2. 為什么是window.onload,而不是document.onload? (Question 3)

按上面那個問題的解釋,明明是整個document里面完全加載后觸發,那為什么是window.onload(在window上是什么鬼),而不是document.onload?

還是按《JavaScript高級程序設計》上解釋:

根據“DOM2級事件”規范,應該是在document而非window上面觸發load事件。但是,所有瀏覽器都在window上面實現了該事件,以確保向后兼容。

結論:道理是這么道理,但大家都這么干了。

jQuery document ready

從原生js來講,並沒有ready這種事件。

那么 document ready到底是什么ready了(Question 4)

按資料說明,這個事件指的是文檔結構(DOM)加載完成觸發的。

PS:這解釋還算合理,就放過這問題。

那jQuery怎么實現$(document).ready(Question 5)

下面我嘗試解析jquery3.0.0版本(兼容IE9+,現代瀏覽器)里面關於ready事件的實現!

注意:

版本我選擇比較新的3.0.0,相比於較舊版本(例1.x)的,里面的實現會簡單一些,因為舍棄一些兼容代碼。不過實現的原理是一樣,倒不需要多個版本都詳看。

原理:

在jquery腳本加載的時候,會監聽DOMContentLoaded事件(監聽load是補救后路)。當事件觸發時候,會執行ready事件的回調。

代碼:

var readyList = jQuery.Deferred();
 
//保存所有DOM加載完后執行的函數。
jQuery.fn.ready = function( fn ) {
     readyList.then( fn );
     return this;
};
 
jQuery.extend( {
 
     //標記DOM ready事件是否觸發過。
     isReady: false,
 
     // A counter to track how many items to wait for before
     // the ready event fires. See #6781
     readyWait: 1,
 
     // Hold (or release) the ready event
     holdReady: function( hold ) {
          if ( hold ) {
               jQuery.readyWait++;
          } else {
               jQuery.ready( true );
          }
     },
 
     // Handle when the DOM is ready
     ready: function( wait ) {
 
          // Abort if there are pending holds or we're already ready
          if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
               return;
          }
 
          // Remember that the DOM is ready
          jQuery.isReady = true;
 
          // 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.resolveWith( document, [ jQuery ] );
     }
} );
 
jQuery.ready.then = readyList.then;
 
// The ready event handler and self cleanup method
function completed() {
     document.removeEventListener( "DOMContentLoaded", completed );
     window.removeEventListener( "load", completed );
     jQuery.ready();
}
 
// Catch cases where $(document).ready() is called
// after the browser event has already occurred.
// Support: IE <=9 - 10 only
// Older IE sometimes signals "interactive" too soon
if ( document.readyState === "complete" ||
     ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {
 
     // Handle it asynchronously to allow scripts the opportunity to delay ready
     window.setTimeout( jQuery.ready );
 
} else {
     // Use the handy event callback
     document.addEventListener( "DOMContentLoaded", completed );
 
     //其實最奸詐是這里,在不支持DOMContentLoaded事件的瀏覽器,用load事件代替
     // A fallback to window.onload, that will always work
     window.addEventListener( "load", completed );
}

特別地方:

1. 注冊事件是用addEventListener,所以該jquery版本應該是只支持IE9+。

2. jQuery的ready,還能ready什么?(Question 6)

ready函數僅能用於當前document,因此無需選擇器,所以不能ready其他元素。

三種姿勢使用該函數:

$(document).ready(function)
$().ready(function)
$(function)

誰更快?

jQuery的document ready就一定比window.onload快嗎?(Question 7)

我寫了一個例子來實驗:

<!DOCTYPE HTML>
<html lang="en-US">
<head>
<meta charset="UTF-8"/>
<title>加載時機</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.js"
onload="console.log('jquery.js loaded')"></script>
<script>
     console.log('define functions');
     function load(type, info){
          console.log(type + ' onload ' + (info || ""), new Date().getTime());
     }
     $(document).ready(function () {
          load('document ready');
     });
     document.onload = function () {
          load('document');
     };    
     window.onload = function () {
          load('window');
     };
     window.addEventListener("load",function(){
          load('window addEventListener');
     });
     document.addEventListener( "DOMContentLoaded", function () {
          load('DOMContentLoaded');
     });
</script>
</head>
     <body onload="load('body')">
          <div onload="load('text')">test</div>
          <img onload="load('img',1)" src="http://www.deskcar.com/desktop/else/2013714232149/17.jpg" />
          <img onload="load('img',2)" src="http://www.deskcar.com/desktop/else/2013714232149/16.jpg" />
          <script onload="load('js')" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.2.0/react.min.js"></script>
     </body>
</html>

這種大體有兩種結果:

首次加載:

二次加載:

第一種情況非常符合我們的想法,ready比onload快,順序也比較合理。

而第二種情況就有些怪異,應該依照上面jquery ready事件的實現,那ready應該要DOMContentLoaded后面啊。我思來想去,我覺得這是個誤會,由於二次加載時利用到緩存,導致文件資源都很快加載,各個事件觸發的時間非常相近,順序也不定,就給人一種ready順序不對之感,大家應該發現這幾個事件都是在幾十毫秒之內觸發。

PS:js執行需要時間,幾十毫秒不同的順序我覺得很正常。另個嘗試幾次,二次加載順序確實會有變化,但時間都很相近。

所以,jQuery的document ready不一定比window.onload快執行。

為什么外部script文件放頁面內容后面好?

script執行順序:

《JavaScript高級程序設計》說過——無論如何包含代碼,只要不存在defer和async屬性,瀏覽器都會按照<script>元素在頁面中出現的先后順序對它們依次進行解析。換句話說,在第一個<script>元素包含的代碼解析完成后,第二個<script>包含代碼才會被解析,然后才是第三個.....

放head元素里:

<!DOCTYPE HTML>
<html lang="en-US">
<head>
<meta charset="UTF-8"/>
<title>Example</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.2.0/react.min.js"></script>
</head>
     <body>
    
     </body>
</html>

在head元素里包含所有JavaScript文件,就必須等到全部JavaScript代碼都被下載、解析和執行完成以后,才能呈現頁面的內容(瀏覽器在遇到<body>標簽時才開始呈現內容)。在需要很多JavaScript文件時候,瀏覽器呈現頁面會出現明顯的延遲,延時期間瀏覽器是一片空白。

所以,外部script文件放頁面內容后面。這樣,在解析JavaScript代碼之前,頁面內容將完全呈現出來。

一定是放頁面內容后面嗎?

有種情況是JavaScript放哪里都一樣的,那就是內容是依賴JavaScript的執行渲染時候,放哪都一樣。

 

總結

雖然這篇文章是簡單的問題,但有時我們就是連簡單的東西都沒搞懂,還以為我們懂了。


本文為原創文章,轉載請保留原出處,方便溯源,如有錯誤地方,謝謝指正。

本文地址 :http://www.cnblogs.com/lovesong/p/5641834.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM