文件:DOMContentLoaded 事件

Baseline 已廣泛支援

此特性已相當成熟,可在許多裝置和瀏覽器版本上使用。自 ⁨2015 年 7 月⁩以來,各瀏覽器均已提供此特性。

當 HTML 文件被完全解析,並且所有延遲指令碼(<script defer src="…"><script type="module">)都已下載和執行時,會觸發 DOMContentLoaded 事件。它不等待其他內容(如影像、子框架和非同步指令碼)完成載入。

DOMContentLoaded 不會等待樣式表載入,但是延遲指令碼 等待樣式表,並且 DOMContentLoaded 事件會在延遲指令碼之後排隊。此外,非延遲或非非同步指令碼(例如 <script>)會等待已解析的樣式表載入。

另一個事件 load 應該只用於檢測頁面是否完全載入。將 load 用於 DOMContentLoaded 更合適的地方是一個常見的錯誤。

通常,為了避免在它所操作的 DOM 完全構建之前執行指令碼,你只需將指令碼放在文件主體的末尾,緊鄰結束的 </body> 標籤之前,而無需將其包裝在事件監聽器中。

此事件不可取消。

語法

addEventListener() 等方法中使用事件名稱。

js
addEventListener("DOMContentLoaded", (event) => { })

注意:此事件沒有 onDOMContentLoaded 事件處理程式屬性。

事件型別

一個通用的 Event

示例

基本用法

js
document.addEventListener("DOMContentLoaded", (event) => {
  console.log("DOM fully loaded and parsed");
});

延遲 DOMContentLoaded

html
<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 之前等待指令碼執行,因此你始終可以確保監聽器中的設定邏輯將執行。

單獨考慮以下指令碼檔案

js
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 解析器完成之後,但在帶有 defertype="module" 的指令碼執行之前設定為 "interactive"DOMContentLoaded 在這些指令碼執行之後,但在帶有 async 的指令碼執行之前觸發。document.readyState 在非同步指令碼執行之後設定為 "complete"。這意味著在延遲和模組指令碼執行期間,document.readyState"interactive",但仍然可以附加 DOMContentLoaded 監聽器並使其像往常一樣觸發。實際上,除非 doSomething() 依賴於其他延遲/模組指令碼設定的一些全域性狀態,否則稍微早一點執行 doSomething() 沒什麼問題。

即時示例

HTML

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

js
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
# 停止解析

瀏覽器相容性

另見