DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> JavaScript入門知識 >> 關於JavaScript >> JS、CSS以及img對DOMContentLoaded事件的影響
JS、CSS以及img對DOMContentLoaded事件的影響
編輯:關於JavaScript     

前端的純技術就是對規范的認知

什麼是DOMContentLoaded事件?

首先想到的是查看W3C的HTML5規范,DOMContentLoaded事件在什麼時候觸發:

Once the user agent stops parsing the document, the user agent must run the following steps:
1. Set the current document readiness to “interactive” and the insertion point to undefined.
Pop all the nodes off the stack of open elements.
2. If the list of scripts that will execute when the document has finished parsing is not empty, run these substeps:
2.1 Spin the event loop until the first script in the list of scripts that will execute when the document has finished parsing has its “ready to be parser-executed” flag set and the parser's Document has no style sheet that is blocking scripts.
2.2 Execute the first script in the list of scripts that will execute when the document has finished parsing.
2.3 Remove the first script element from the list of scripts that will execute when the document has finished parsing (i.e. shift out the first entry in the list).
2.4 If the list of scripts that will execute when the document has finished parsing is still not empty, repeat these substeps again from substep 1.
3. Queue a task to fire a simple event that bubbles named DOMContentLoaded at the Document.

規范總是那麼的晦澀,但至少有一點是可以明確了的,就是在JS(不包括動態插入的JS)執行完之後,才會觸發DOMContentLoaded事件。

接下來看看MDN上有關DOMContentLoaded事件的文檔:

The DOMContentLoaded event is fired when the document has been completely loaded and parsed, without waiting for stylesheets, images, and subframes to finish loading
Note: Stylesheet loads block script execution, so if you have a <script> after a <link rel="stylesheet" ...>, the page will not finish parsing – and DOMContentLoaded will not fire – until the stylesheet is loaded.

這麼看來,至少可以得出這麼一個理論:DOMContentLoaded事件本身不會等待CSS文件、圖片、iframe加載完成。
它的觸發時機是:加載完頁面,解析完所有標簽(不包括執行CSS和JS),並如規范中所說的設置 interactive 和執行每個靜態的script標簽中的JS,然後觸發。
而JS的執行,需要等待位於它前面的CSS加載(如果是外聯的話)、執行完成,因為JS可能會依賴位於它前面的CSS計算出來的樣式。

實踐是檢驗真理的唯一標准

實驗1:DOMContentLoaded事件不直接等待CSS文件、圖片的加載完成

index.html:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title></title>
  <link rel="stylesheet" type="text/css" href="./css/main.css" rel="external nofollow" rel="external nofollow" >
</head>
<body>
  <p>Content</p>
  <img src="./img/chrome-girl.jpg">
</body>
</html>

71fca778-a249-11e3-8824-2aae4440c857

圖一

如果頁面中沒有script標簽,DOMContentLoaded事件並沒有等待CSS文件、圖片加載完成。

Chrome開發者工具的Timeline面板可以幫我們記錄下浏覽器的一舉一動。圖一中紅色小方框中的藍線,表示DOMContentLoaded事件,它右邊的紅線和綠線分別表示load事件和First paint,鼠標hover在這些線露出灰色方框下面的一小部分時就會出現帶有說明文字的tips(這交互夠反人類的對吧!)。

實驗2:DOMContentLoaded事件需要等待JS執行完才觸發

index.html:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title></title>
  <script type="text/javascript">
    console.timeStamp('Inline script before link in head');
    window.addEventListener('DOMContentLoaded', function(){
      console.timeStamp('DOMContentLoaded event');
    });
  </script>
  <link rel="stylesheet" type="text/css" href="./css/main.css" rel="external nofollow" rel="external nofollow" >
  <script type="text/javascript">
    console.timeStamp('Inline script after link in head');
  </script>
</head>
<body>
  <p>Content</p>
  <img src="./img/chrome-girl.jpg">
  <script type="text/javascript" src="./js/main.js"></script>
</body>
</html>

main.js:
console.timeStamp('External script after link in body');

dcf399e8-a252-11e3-92c1-c3dbad820909

圖二

如果頁面中靜態的寫有script標簽,DOMContentLoaded事件需要等待JS執行完才觸發。

而script標簽中的JS需要等待位於其前面的CSS的加載完成。

console.timeStamp() 可以向Timeline中添加一條記錄,並對應上方的一條黃線。

從圖二中可以看出,在CSS之前的JS立刻得到了執行,而在CSS之後的JS,需要等待CSS加載完後才執行,比較明顯的是main.js早就加載完了,但還是要等main.css加載完才能執行。而DOMContentLoaded事件,則是在JS執行完後才觸發。滑動Timeline面板中表示展示區域的滑塊,如圖三,放大後即可看到表示DOMContentLoaded事件的藍線(之前跟黃線和綠線靠的太近了),當然,通過 console.timeStamp() 向TimeLine中添加的記錄也可證明其觸發時間。

910b5c2c-a253-11e3-995d-e19fb254cf4e

圖三

現代浏覽器會並發的預加載CSS, JS,也就是一開始就並發的請求這些資源,但是,執行CSS和JS的順序還是按原來的依賴順序(JS的執行要等待位於其前面的CSS和JS加載、執行完)。先加載完成的資源,如果其依賴還沒加載、執行完,就只能等著。

實驗3:img何時開始解碼、繪制?


從圖三中我們可以發現一個有趣的地方:img的請求老早就發出了,但延遲了一段時間才開始解碼。如圖二、圖三中的紅框所示,截圖中只框出了一部分表示解碼的記錄,而實際上這些表示解碼的記錄一直持續到img加載結束,如圖四所示,img是一邊加載一邊解碼的:

7384a57a-a256-11e3-9c4a-b857956eaeed

圖三

現代浏覽器會並發的預加載CSS, JS,也就是一開始就並發的請求這些資源,但是,執行CSS和JS的順序還是按原來的依賴順序(JS的執行要等待位於其前面的CSS和JS加載、執行完)。先加載完成的資源,如果其依賴還沒加載、執行完,就只能等著。

實驗3:img何時開始解碼、繪制?

從圖三中我們可以發現一個有趣的地方:img的請求老早就發出了,但延遲了一段時間才開始解碼。如圖二、圖三中的紅框所示,截圖中只框出了一部分表示解碼的記錄,而實際上這些表示解碼的記錄一直持續到img加載結束,如圖四所示,img是一邊加載一邊解碼的:

7384a57a-a256-11e3-9c4a-b857956eaeed
圖四

抱著“猜想——驗證”的想法,我猜想這是因為img這個資源是否需要展現出來,需要等 所有的JS和CSS的執行完 才知道,因為main.js可能會執行某些DOM操作,比如刪除這個img元素,或者修改其src屬性,而CSS可能會將其 display: none

image
圖五

image
圖六

image
圖七

圖五中沒有JS和CSS,img的數據一接收到就馬上開始解碼了。
圖六中沒有JS,但img要等到CSS加載完才開始解碼。
圖七的代碼跟圖六的代碼唯一的區別是CSS把img給 display: none; ,這使得img雖然請求了,但根本沒有進行解碼。
這說明,img是否需要解碼、繪圖(paint)出來,確實需要等CSS加載、執行完才能知道。也就是說,CSS會阻塞img的展現!那麼JS呢?

image
圖八

圖八對應的代碼:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title></title>
  <script type="text/javascript">
    console.timeStamp('Inline script in head');
    window.addEventListener('DOMContentLoaded', function(){
      console.timeStamp('DOMContentLoaded event');
    });
  </script>
</head>
<body>
  <p>Content</p>
  <img src="./img/chrome-girl.jpg">
  <script type="text/javascript" src="./js/main.js"></script>
</body>
</html>

非常令人驚訝,在有JS而沒有CSS的頁面中,img居然能夠在收到數據後就立刻開始解碼、繪圖(paint),也就是說,JS並沒有阻塞img的展現!這跟我們以前理解的JS會阻塞img資源的傳統觀念不太一樣,看來Chrome對img的加載和展現做了新的優化。

我們常用的jQuery的 $(document).ready() 方法,就是對DOMContentLoaded事件的監聽(當然,其內部還會通過模擬DOMContentLoaded事件和監聽onload事件來提供降級方案)。通常推薦在DOMContentLoaded事件觸發的時候為DOM元素注冊事件。所以盡快的讓DOMContentLoaded事件觸發,就意味著能夠盡快讓頁面可交互:

減小CSS文件體積,把單個CSS文件分成幾個文件以並行加載,減少CSS對JS的阻塞時間

次要的JS文件,通過動態插入script標簽來加載(動態插入的script標簽不阻塞DOMContentLoaded事件的觸發)

CSS中使用的精靈圖,可以利用對img的預加載,放在html中跟CSS文件一起加載

在做實驗的過程中,感覺Chrome開發者工具的Timeline面板非常強大,浏覽器的一舉一動都記錄下來。以前我們前端開發要想理解、探索浏覽器的內部行為,或者摸著石頭過河的做黑盒測試,或者事倍功半的研究浏覽器源碼,唯一高效點的做法就是學習別人的研究經驗,看老外的文章,但浏覽器的發展日新月異(比如這次實驗發現的JS不阻塞img的展現),別人的經驗始終不是最新、最適合的,關鍵是要結合自己的業務、需求場景,有針對性的做分析和優化。

PS.
以上測試環境為windows/chrome,並用Fiddler模擬慢速網絡

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved