文件:DOMContentLoaded 事件
當 HTML 文件被完全解析,並且所有延遲指令碼(<script defer src="…"> 和 <script type="module">)都已下載和執行時,會觸發 DOMContentLoaded 事件。它不等待其他內容(如影像、子框架和非同步指令碼)完成載入。
DOMContentLoaded 不會等待樣式表載入,但是延遲指令碼 會 等待樣式表,並且 DOMContentLoaded 事件會在延遲指令碼之後排隊。此外,非延遲或非非同步指令碼(例如 <script>)會等待已解析的樣式表載入。
另一個事件 load 應該只用於檢測頁面是否完全載入。將 load 用於 DOMContentLoaded 更合適的地方是一個常見的錯誤。
通常,為了避免在它所操作的 DOM 完全構建之前執行指令碼,你只需將指令碼放在文件主體的末尾,緊鄰結束的 </body> 標籤之前,而無需將其包裝在事件監聽器中。
此事件不可取消。
語法
在 addEventListener() 等方法中使用事件名稱。
addEventListener("DOMContentLoaded", (event) => { })
注意:此事件沒有 onDOMContentLoaded 事件處理程式屬性。
事件型別
一個通用的 Event。
示例
基本用法
document.addEventListener("DOMContentLoaded", (event) => {
console.log("DOM fully loaded and parsed");
});
延遲 DOMContentLoaded
<script>
document.addEventListener("DOMContentLoaded", (event) => {
console.log("DOM fully loaded and parsed");
});
for (let i = 0; i < 1_000_000_000; i++);
// This synchronous script is going to delay parsing of the DOM,
// so the DOMContentLoaded event is going to launch later.
</script>
檢查載入是否已完成
有時你的指令碼可能會在 DOMContentLoaded 事件已經觸發後執行。這通常發生在指令碼非同步執行的情況下。常見場景包括:
- 文件載入後動態匯入的模組。
- 透過
<script async>包含的指令碼。 - 動態注入到頁面中的指令碼。
- 非同步操作(例如
await fetch(...))之後恢復的程式碼,包括模組中的頂級 await 之後。
在這些情況下,你應該在新增 DOMContentLoaded 監聽器之前檢查文件的 readyState,否則你的設定邏輯可能根本不會執行。對於初始標記中已經存在的同步指令碼(沒有 async),這種情況不會發生。文件在觸發 DOMContentLoaded 之前等待指令碼執行,因此你始終可以確保監聽器中的設定邏輯將執行。
單獨考慮以下指令碼檔案
function doSomething() {
console.info("DOM loaded");
}
if (document.readyState === "loading") {
// Loading hasn't finished yet
document.addEventListener("DOMContentLoaded", doSomething);
} else {
// `DOMContentLoaded` has already fired
doSomething();
}
指令碼無法強制其由 HTML 包含的方式。如果它透過 <script async> 包含,或者它是動態注入的,那麼當它執行時,DOMContentLoaded 已經觸發。為了確保 doSomething() 在指令碼載入時始終執行,我們需要有兩條路徑,一條是如果文件已經載入則立即執行 doSomething,另一條是在文件載入後執行 doSomething。
注意:這裡沒有競態條件——文件不可能在 if 檢查和 addEventListener() 呼叫之間載入。JavaScript 具有執行到完成語義,這意味著如果文件在事件迴圈的某個特定時刻正在載入,它就不能在下一個週期之前載入完成,屆時 doSomething 處理程式已經附加並將被觸發。
注意:document.readyState 在 HTML 解析器完成之後,但在帶有 defer 或 type="module" 的指令碼執行之前設定為 "interactive"。DOMContentLoaded 在這些指令碼執行之後,但在帶有 async 的指令碼執行之前觸發。document.readyState 在非同步指令碼執行之後設定為 "complete"。這意味著在延遲和模組指令碼執行期間,document.readyState 是 "interactive",但仍然可以附加 DOMContentLoaded 監聽器並使其像往常一樣觸發。實際上,除非 doSomething() 依賴於其他延遲/模組指令碼設定的一些全域性狀態,否則稍微早一點執行 doSomething() 沒什麼問題。
即時示例
HTML
<div class="controls">
<button id="reload" type="button">Reload</button>
</div>
<div class="event-log">
<label for="eventLog">Event log:</label>
<textarea
readonly
class="event-log-contents"
rows="8"
cols="30"
id="eventLog"></textarea>
</div>
JavaScript
const log = document.querySelector(".event-log-contents");
const reload = document.querySelector("#reload");
reload.addEventListener("click", () => {
log.textContent = "";
setTimeout(() => {
window.location.reload(true);
}, 200);
});
window.addEventListener("load", (event) => {
log.textContent += "load\n";
});
document.addEventListener("readystatechange", (event) => {
log.textContent += `readystate: ${document.readyState}\n`;
});
document.addEventListener("DOMContentLoaded", (event) => {
log.textContent += "DOMContentLoaded\n";
});
結果
規範
| 規範 |
|---|
| HTML # 停止解析 |
瀏覽器相容性
載入中…