domReady的了解
domReady
是名為
DOMContentLoaded
事件的别稱,當初始的
HTML
文檔被完全加載和解析完成之後,
DOMContentLoaded
事件被觸發,而無需等待樣式表、圖像和子架構的完全加載。
描述
浏覽器渲染
DOM
結構是有一定順序的,雖然不同浏覽器的實作各有不同,但是基本流程都大緻相同:
- 自上而下,首先解析
标簽,生成HTML
。DOM Tree
- 在解析到
或者<link>
标簽時,開始解析<style>
,生成CSS
,值的注意的是此時解析CSSOM
标簽與解析HTML
是并行執行的。CSS
- 當遇到
标簽後,浏覽器會立即開始解析腳本,并停止解析文檔,因為腳本有可能會改動<script>
與DOM
,繼續解析會浪費資源,是以應當将CSS
标簽放于<script>
後。<body></body>
- 當
DOM Tree
生成後,将兩者結合進行布局,計算它們的大小位置等布局資訊,形成一個能夠表示這所有資訊的内部表示模型,可稱為渲染樹CSSOM
render tree
- 根據計算好的資訊繪制整個頁面,系統會周遊渲染樹,并調用
方法,将内容顯示在螢幕上。paint
在浏覽器解析
DOM
結構的過程中是存在阻塞過程的:
- 解析
過程中會阻塞浏覽器的解析過程,準确來說解析渲染過程與解析JavaScript
的過程是互斥的。JavaScript
-
加載解析時不會阻塞CSS
樹的解析過程,這兩個解析過程是可以并行的,但是DOM
加載過程中是不能進行CSS
的解析的,也就是說JavaScript
加載過程中是會阻塞CSS
的解析,此外因為生成JavaScript
時需要Render Tree
,是以在CSSOM
解析完成而DOM Tree
未完成時不會繼續生成CSSOM
Render Tree
-
結構同樣不會阻塞HTML
解析的過程,也同樣不會和CSS
的解析過程并行執行,并且JavaScript
解析未完成而DOM Tree
完成時同樣不會繼續生成CSSOM
Render Tree
- 使用異步加載的
标簽是不會阻塞<script>
解析的,當然其就不會阻塞DOM
事件的觸發,但是依舊會阻塞DOMContentLoaded
事件的觸發。load
再來看一下
DOMContentLoaded
事件與
load
事件的觸發時機:
- 當初始的
HTML
事件被觸發,而無需等待樣式表、圖像和子架構的完全加載。關于觸發的時機,如果文檔中全部為DOMContentLoaded
HTML
則CSS
事件無需等到DomContentLoaded
加載完畢即可觸發;當CSS
都在Js
之前CSS
DomContentLoaded
加載完畢即可觸發,當然解析CSS
CSS
是需要等待前邊的DOM
解析完畢的;當Js
在Js
之後時,則CSS
事件需等到DomContentLoaded
CSS
加載完畢才能夠觸發,上文也提到了Js
的加載會阻塞CSS
的加載,而Js
标簽本身也屬于Js
結構,必須等待其加載完成之後才能觸發DOM
事件;異步加載的DomContentLoaded
标簽不會阻塞<script>
事件。DOMContentLoaded
- 當整個頁面及所有依賴資源如樣式表和圖檔都已完成加載時,将觸發
事件。不使用動态加載的load
同樣會阻塞<iframe>
事件,此外即使是異步加載的load
标簽同樣會阻塞<script>
load
在各種條件下重新整理一下頁面加載的過程,主要是在于
DOMContentLoaded
load
事件觸發的時間線:
-
HTML
,此時DOM Tree
document.readyState = "loading"
-
<link>
<style>
CSS
CSSOM
HTML
CSS
- 解析到沒有設定異步加載的
的時候,阻塞文檔解析,等待<script>
腳本加載并且執行完成後,才會繼續解析文檔。Js
- 解析到異步
的時候不阻塞解析文檔,繼續向下解析,<script>
屬性會使defer
檔案等待Js
建構完成之後再執行,而DOM Tree
async
檔案在下載下傳完成後立即執行。Js
- 解析文檔的時候遇到需要加載外部資源例如圖檔時,先解析這個節點,根據
建立加載線程,異步加載圖檔資源,不阻塞解析文檔,當然浏覽器對于一個域名能夠開啟最大的線程數量會有限制。src
- 文檔解析完成,
document.readyState = "interactive"
- 設定為
屬性的defer
腳本開始按照順序執行。<script>
- 觸發
DOMContentLoaded
- 等待設定為
async
以及圖檔與<script>
等加載,直至頁面完全加載完成。<iframe>
-
事件觸發,load
document.readyState = "complete"
調用
有些時候我們希望盡快介入對
DOM
的幹涉,此時調用
DOMContentLoaded
事件顯然更加合适,而為了處理各種浏覽器,需要對其進行相容處理。
- 對于支援
的浏覽器使用DOMContentLoaded
DOMContentLoaded
- 如果是小于
版本的525
則通過輪詢Webkit
來實作。document.readyState
- 對于舊版本的
浏覽器使用IE
發現的著名Diego Perini
hack
/* https://www.cnblogs.com/JulyZhang/archive/2011/02/12/1952484.html */
/*
* 注冊浏覽器的DOMContentLoaded事件
* @param { Function } onready [必填]在DOMContentLoaded事件觸發時需要執行的函數
* @param { Object } config [可選]配置項
*/
function onDOMContentLoaded(onready, config) {
//浏覽器檢測相關對象,在此為節省代碼未實作,實際使用時需要實作。
//var Browser = {};
//設定是否在FF下使用DOMContentLoaded(在FF2下的特定場景有Bug)
this.conf = {
enableMozDOMReady: true
};
if (config)
for (var p in config)
this.conf[p] = config[p];
var isReady = false;
function doReady() {
if (isReady) return;
//確定onready隻執行一次
isReady = true;
onready();
}
/*IE*/
if (Browser.ie) {
(function() {
if (isReady) return;
try {
document.documentElement.doScroll("left");
} catch (error) {
setTimeout(arguments.callee, 0);
return;
}
doReady();
})();
window.attachEvent('onload', doReady);
}
/*Webkit*/
else if (Browser.webkit && Browser.version < 525) {
(function() {
if (isReady) return;
if (/loaded|complete/.test(document.readyState))
doReady();
else
setTimeout(arguments.callee, 0);
})();
window.addEventListener('load', doReady, false);
}
/*FF Opera 高版webkit 其他*/
else {
if (!Browser.ff || Browser.version != 2 || this.conf.enableMozDOMReady)
document.addEventListener("DOMContentLoaded", function() {
document.removeEventListener("DOMContentLoaded", arguments.callee, false);
doReady();
}, false);
window.addEventListener('load', doReady, false);
}
}
每日一題
https://github.com/WindrunnerMax/EveryDay
參考
https://juejin.im/post/6844903667733118983
https://juejin.im/post/6844903535314731021
https://juejin.im/post/6844903623583891469
https://juejin.im/post/6844904072340832264
https://juejin.im/post/6844904176569286669
https://www.cnblogs.com/caizhenbo/p/6679478.html
https://www.cnblogs.com/rubylouvre/p/4536334.html
https://developer.mozilla.org/zh-CN/docs/Web/Events/DOMContentLoaded
https://gwjacqueline.github.io/%E5%BC%82%E6%AD%A5%E5%8A%A0%E8%BD%BDjs/