頁面生命周期內,有兩個非常重要的事件:
1.DOMContentLoaded
此時瀏覽器已經完全加載了HTML文件,并且DOM樹已經生成好了。但是其他外部資源,如樣式文件、圖片、字體等并沒有加載好
2.Load
此時瀏覽器已經將所有的資源都加載完畢,可以正確讀取頁面中的資源。
兩個事件標識了兩種不同的時刻:
1.DOMContentLoaded
DOM已經完成加載,此時可以為這些DOM元素綁定事件,初始化接口等
2.Load
其他外部資源均已加載完成,可以正確讀出這些資源的信息,如圖片的寬高等
DOMContentLoaded
使用addEventListener
監聽該事件:
document.addEventListener('DOMContentLoaded', ready)
舉一個??
<script>
function ready() {
alert('DOM is ready');
var img = document.getElementById('img')
// 由于圖片屬于外部資源,尚未下載完成,因此暫無法讀到數據,所以結果是0x0
alert(`Image size: ${img.offsetWidth}x${img.offsetHeight}`);
}
document.addEventListener("DOMContentLoaded", ready);
</script>

雖然DOMContentLoaded
如我們期望的那樣工作,但仍在個別情況下有所不同
異步scripts
當解析器(HTML Paser)讀取到<script>...</script>
內聯標簽時,會阻塞DOM構建,它會立即執行腳本,原因是可能這些腳本會影響DOM,因此需要等到這些腳本都執行完成了,才會觸發DOMContentLoaded
事件。
外部腳本(<script src="...">...</script>
)也是如此,瀏覽器需要等到加載后再執行完成才可以繼續執行。
是否可以讓外部引用的腳本延后執行,答案是可以的。async
和defer
屬性可以讓外部腳本延后執行,而不阻塞瀏覽器解析文檔。async
和defer
僅在<script src="...">...</script>
情形下起作用,對內聯腳本是無效的。用戶可以在腳本完成加載前,就可以看到頁面了,提升了用戶體驗。
async & defer
這兩個屬性都是告訴瀏覽器,標記的腳本不需要等待它加載完成,瀏覽器可以繼續完成DOM構建和渲染
兩者的區別如下:
async | defer | |
---|---|---|
執行順序 | 誰先完成下載誰先執行 | 執行順序始終遵循加載的順序,即使先下載完成了 |
DOMContentLoaded | 如果頁面加載時間較長,腳本可能會先執行;絕大多數會在DOMContentLoaded之后執行,但一定是在Load事件之前執行 | 腳本會在DOMContentLoaded之前執行,但不阻塞瀏覽器加載和解析文檔,即defer腳本與瀏覽器加載順序無關先后 |
因此async
更符合一些應用場景。
樣式阻塞
對于外部樣式文件而言,并不會影響到DOMCotentLoaded事件,它并不會等待外部樣式文件加載完成。
但是!?。?,如果一個腳本是緊跟著link樣式出現,那么這個腳本就會等樣式加載完成了。如下:
<link type="text/css" rel="stylesheet" href="style.css">
<script>
// the script doesn't not execute until the stylesheet is loaded
alert(getComputedStyle(document.body).marginTop);
</script>
原因是瀏覽器猜測腳本可能會讀取一些樣式信息,如位置、顏色,顯然腳本就需要等到樣式的加載完成了。
這也是為什么我們最好把腳本文件放到HTML文檔的最后再加載,而不要把腳本放在head標簽里,除非你清楚放在head標簽里是必要的。
瀏覽器表單自動填充
這個是順便引出的話題,你知道瀏覽器內置的表單自動填充在什么時候完成么?沒錯,就是DOMContentLoaded觸發的時候
window.onload
當所有外部資源都完成加載后,瀏覽器觸發load事件,此時可以讀取外部資源信息了
<script>
window.onload = function() {
alert('Page loaded');
// image is loaded at this time
alert(`Image size: ${img.offsetWidth}x${img.offsetHeight}`);
};
</script>

Chrome DevTool中的load
我們通??梢酝ㄟ^DOMContentLoaded的時間去衡量一個頁面加載的速度,因為此時用戶已經可以看到這個頁面,并可以有一些交互了。
兼容性
以上提到的使用document.addEventListener
監聽DOMCotentLoaded
事件,在IE9+都是有效的。需要兼容IE低版本
// 摘抄自jQuery源碼
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 );
}
比較粗暴,反復去嗅探是否可以執行ready函數。有興趣去讀讀jQuery源碼,很有意思