本文 AI 產出,尚未審核

JavaScript – DOM 與瀏覽器 API

單元:DOM 結構與節點類型


簡介

在前端開發中,DOM(Document Object Model) 是連結 HTML 與 JavaScript 的橋樑。瀏覽器會把一張 HTML 文件解析成一棵樹狀結構,開發者便可以透過程式碼「讀取」或「修改」這棵樹,從而動態改變頁面的內容與行為。

如果不了解 DOM 的基本結構與各種節點(Node)類型,就很難寫出可維護、效能良好的程式。相反地,掌握這些概念後,我們可以更精準地操作元素、插入文字、移除註解,甚至在不觸發重排(reflow)的情況下批次更新頁面。

本篇文章將從 DOM 樹的層級節點類型、以及 常見操作 三個面向,深入淺出地說明它們的特性與實務應用,幫助你在開發過程中少走彎路、寫出更乾淨的程式碼。


核心概念

1. DOM 樹的基本結構

DOM 把一個 HTML 文件視為一棵節點樹(Node Tree),根節點是 Document 物件,下面依序是:

層級 節點名稱 說明
0 Document 代表整個文件本身,唯一且不可被刪除。
1 DocumentType (<!DOCTYPE html>) 定義文件類型,位於根節點之下。
2 Element (<html>) 主要的 HTML 標籤,通常稱為 根元素
3+ ElementTextComment 依照 HTML 結構往下展開。

小技巧:在瀏覽器 console 中輸入 document.documentElement,即可直接取得 <html> 元素,這是檢視整棵樹的起點。

2. Node 介面的屬性與方法

所有節點(不論是 ElementTextComment)都繼承自 Node 介面,常用的屬性包括:

屬性 說明
nodeType 以數值表示的節點類型(1=Element、3=Text、8=Comment、9=Document 等)。
nodeName 節點的名稱,對於元素是標籤名稱(大寫),文字節點則是 #text
childNodes 包含所有子節點的 NodeList(包括文字與註解)。
parentNode 父節點參考。
firstChild / lastChild 第一或最後一個子節點。

提醒children 只返回元素節點(Element),而 childNodes 會把文字、註解等全部列出。

3. 常見節點類型

類型 nodeType 範例 典型用途
Document 9 document 整體文件操作(如 createElement)。
DocumentType 10 <!DOCTYPE html> 指定文件類型,通常不需要操作。
Element 1 <div>、<p> 大部分 UI 操作的目標。
Text 3 文字節點 "Hello" 內文、文字節點的插入與修改。
Comment 8 <!-- comment --> 開發除錯或說明用,通常不會顯示。
DocumentFragment 11 document.createDocumentFragment() 暫存多筆節點,批次插入以降低重排。

程式碼範例

以下範例示範 5 種常見操作,每段程式碼均附有說明註解,方便直接貼上測試。

1️⃣ 取得不同類型的節點與屬性

// 取得根元素 <html>
const htmlEl = document.documentElement;   // Element
console.log(htmlEl.nodeName);              // "HTML"
console.log(htmlEl.nodeType);              // 1 (Element)

// 取得 <body> 裡的第一個子節點(可能是文字或註解)
const firstChild = document.body.firstChild;
console.log(firstChild.nodeName);          // "#text"、"#comment" 或 標籤名稱
console.log(firstChild.nodeType);          // 3、8 或 1

// 只想拿到真正的元素子節點,可使用 children
console.log(document.body.children.length);   // 只算 <div>、<p> … 等元素

2️⃣ 建立與插入文字節點

// 建立 <p> 元素
const p = document.createElement('p');

// 建立文字節點
const txt = document.createTextNode('Hello, DOM!');

// 把文字節點掛到 <p> 裡
p.appendChild(txt);

// 把 <p> 加到 <body> 的最後面
document.body.appendChild(p);

3️⃣ 使用 DocumentFragment 批次插入多筆節點

// 假設要一次加入 100 個 <li>
const ul = document.createElement('ul');
const fragment = document.createDocumentFragment(); // 暫存容器

for (let i = 1; i <= 100; i++) {
  const li = document.createElement('li');
  li.textContent = `項目 ${i}`;
  fragment.appendChild(li); // 只在 fragment 裡操作,不會觸發重排
}

// 最後一次性插入到 <ul>,只產生一次重排
ul.appendChild(fragment);
document.body.appendChild(ul);

4️⃣ 替換、移除與克隆節點

// 取得目標元素
const oldDiv = document.querySelector('#old');

// 建立新元素
const newDiv = document.createElement('div');
newDiv.id = 'new';
newDiv.textContent = '我是新元素';

// 替換 oldDiv 為 newDiv
oldDiv.parentNode.replaceChild(newDiv, oldDiv);

// 複製一份(深層複製會把子節點一起帶走)
const clone = newDiv.cloneNode(true);
clone.id = 'clone';
document.body.appendChild(clone);

5️⃣ 操作註解節點(Comment)

// 建立註解節點
const comment = document.createComment('這是一段說明文字');

// 把註解插入到 <head> 之後
document.head.appendChild(comment);

// 之後若要移除
comment.parentNode.removeChild(comment);

常見陷阱與最佳實踐

陷阱 為什麼會發生 改善方式
使用 innerHTML 直接寫入大量內容 會觸發 HTML 解析樣式重新計算,且易遭 XSS 攻擊。 優先使用 DOM 方法createElementappendChild)或 模板字串 + textContent
遍歷 childNodes 時忘記過濾文字節點 文字節點的 nodeType 為 3,若直接操作可能產生錯誤。 使用 children 只取得元素,或在迴圈中檢查 node.nodeType === 1
頻繁插入單一節點導致重排 每次 appendChild 都會觸發瀏覽器重新計算佈局。 批次操作:先放入 DocumentFragment,最後一次性插入。
直接修改 nodeValue 而非 textContent nodeValue 只適用於文字或註解節點,易忘記節點類型。 對元素使用 textContentinnerText,對文字節點則使用 nodeValue
忘記釋放事件監聽器 移除節點時,若仍保留事件參考會造成記憶體泄漏。 在移除前使用 removeEventListener,或採用 事件委派

最佳實踐小結

  1. 盡量使用 textContent 取代 innerHTML(除非真的需要解析 HTML)。
  2. 批次更新DocumentFragmentrequestAnimationFramesetTimeout 可減少重排。
  3. 檢查 nodeType:操作前確保是正確的節點類型。
  4. 事件委派:把事件掛在父容器,減少大量 addEventListener
  5. 保持語意:使用正確的 HTML 標籤(如 <ul><li>)配合 DOM 操作,讓 SEO 與可存取性更佳。

實際應用場景

場景 需求 相關節點操作 為何重要
動態清單(Todo List) 新增、刪除、勾選項目 createElementappendChildremoveChildclassList.toggle 需要即時更新 UI,且避免頻繁重排。
即時搜尋建議 使用者輸入時即時顯示建議列表 DocumentFragment + cloneNode + textContent 大量建議項目一次渲染,提高效能。
富文字編輯器 插入/刪除文字、圖片、註解 操作 TextElementComment,使用 rangeselection API 必須精確控制文字節點與光標位置。
SPA 路由切換 依路由載入不同的視圖 先清空容器 container.innerHTML = '',再用 appendChild 注入新節點 保持單頁應用的流暢度與記憶體管理。
自訂工具列(Toolbar) 動態開關按鈕、顯示提示 createElement('button')setAttributeaddEventListener 需要即時建立或移除 DOM,且保持語意正確。

總結

DOM 是前端開發的基礎建築,了解 樹狀結構節點類型ElementTextCommentDocumentFragment)以及 Node 介面的屬性與方法,能讓我們在操作頁面時更得心應手。

  • 正確辨識 nodeType,避免把文字節點當作元素處理。
  • 盡量使用 DOM 方法createElementappendChild)取代直接寫入 innerHTML,提升安全性與效能。
  • 使用 DocumentFragment事件委派 等技巧,減少重排與記憶體泄漏。

掌握這些核心概念後,你將能在 Todo List、即時搜尋、SPA 路由 等常見情境中,寫出乾淨、效能佳且易於維護的程式碼。未來若要深入學習更進階的瀏覽器 API(如 IntersectionObserverResizeObserver)或 Virtual DOM,這些基礎都會是最堅實的跳板。祝你在 JavaScript 的世界裡玩得開心、寫得順手!