JavaScript – DOM 與瀏覽器 API
主題:新增 / 移除元素
簡介
在前端開發中,DOM(Document Object Model) 是連結 HTML、CSS 與 JavaScript 的橋樑。透過 DOM,我們可以在執行時動態地建立、修改或刪除頁面元素,讓網站具備即時互動的能力。
無論是加入商品卡片、顯示錯誤訊息,或是依使用者操作即時移除不需要的區塊,都離不開「新增」與「移除」元素的技巧。掌握這些基本操作,不僅能提升開發效率,也能讓程式碼更具可讀性與可維護性。
本篇文章將從 概念說明、實作範例、常見陷阱、最佳實踐 以及 實務應用 四個面向,完整講解在瀏覽器環境下如何安全、有效地新增與移除 DOM 元素。適合剛踏入前端的初學者,也能為已有基礎的開發者提供中級層次的最佳實作參考。
核心概念
1. 何謂「節點」與「元素」
在 DOM 中,節點(Node) 是所有可被操作的基本單位,包含元素節點(Element)、文字節點(Text)與註解節點(Comment)等。本文主要聚焦於 元素節點,即 HTML 標籤所代表的物件,如 <div>、<li>、<button> 等。
2. 常用的新增方法
| 方法 | 說明 | 典型使用情境 |
|---|---|---|
appendChild() |
把子節點加入到父節點的最後面 | 動態加入列表項目 |
insertBefore() |
把子節點插入到參考節點之前 | 在特定位置插入廣告 |
append() / prepend()(HTML5) |
可同時插入文字、HTML 字串或 Node,支援多個參數 | 快速插入多筆內容 |
replaceChild() |
用新節點取代舊節點 | 替換舊的載入動畫 |
Tip:
append()與prepend()會自動將字串轉成 TextNode,若要插入 HTML,請使用insertAdjacentHTML()或innerHTML。
3. 常用的移除方法
| 方法 | 說明 | 典型使用情境 |
|---|---|---|
removeChild() |
從父節點移除指定子節點,並回傳被移除的節點 | 移除已完成的任務項目 |
element.remove()(HTML5) |
直接在目標元素上呼叫,無需知道父節點 | 簡化程式碼的寫法 |
replaceWith()(HTML5) |
用新節點取代當前元素,同時移除舊元素 | 取代舊的按鈕樣式 |
注意:
removeChild()會拋出NotFoundError,若要安全移除請先檢查子節點是否真的存在於父節點之中。
4. 建立新元素的步驟
- 建立節點:
document.createElement(tagName) - 設定屬性與內容:
element.setAttribute()、element.classList、element.textContent、element.innerHTML - 插入 DOM:使用上表的新增方法之一
程式碼範例
以下示範 5 個常見且實用的 新增 / 移除 範例,均附有詳細註解。
範例 1:使用 appendChild() 動態加入清單項目
// 取得 <ul id="todo-list">
const ul = document.getElementById('todo-list');
// 建立 <li> 元素
const li = document.createElement('li');
li.textContent = '購買咖啡豆';
// 加入到 <ul> 最後
ul.appendChild(li);
說明:
appendChild會把新建立的<li>加到父節點的 最後,適合「堆疊」式的清單。
範例 2:使用 insertBefore() 在特定位置插入廣告
const container = document.querySelector('.article-content');
const ad = document.createElement('div');
ad.className = 'ad-banner';
ad.innerHTML = '<a href="https://example.com">點我看優惠</a>';
// 取得要插入前的參考節點(例如第一段落)
const referenceNode = container.querySelector('p');
// 若參考節點不存在,fallback 為末端
container.insertBefore(ad, referenceNode || null);
說明:
insertBefore需要兩個參數——新節點與參考節點。若參考節點為null,等同於appendChild。
範例 3:使用 append() 同時加入多筆內容(文字 + HTML)
const msgBox = document.querySelector('#message-box');
// 同時加入文字、HTML 以及另一個 Node
msgBox.append(
'系統訊息:',
document.createElement('br'), // 換行
'<strong>已完成</strong>', // 會自動轉為 TextNode,若想保留 HTML 用 insertAdjacentHTML
document.createElement('hr')
);
重點:
append允許傳入 任意數量 的參數,非常適合一次性寫入多筆資料。若傳入字串,會被視為 文字節點,不會被解析成 HTML。
範例 4:使用 element.remove() 移除指定卡片(HTML5)
// 假設每張卡片都有 .card class 且包含 .close 按鈕
document.addEventListener('click', function (e) {
if (e.target.matches('.card .close')) {
// 找到最近的 .card 父層
const card = e.target.closest('.card');
// 直接呼叫 remove()
card.remove();
}
});
說明:使用事件委派(event delegation)可以一次監聽所有
.close按鈕,省去為每張卡片綁定個別監聽器。
範例 5:使用 replaceWith() 取代舊的載入動畫
const spinner = document.querySelector('#loading-spinner');
// 載入完成後,用內容取代 spinner
const content = document.createElement('div');
content.id = 'main-content';
content.innerHTML = '<h1>歡迎使用本網站</h1>';
spinner.replaceWith(content);
技巧:
replaceWith同時完成 移除 與 插入,讓程式碼更簡潔。
常見陷阱與最佳實踐
| 陷阱 | 可能的結果 | 解決方案 |
|---|---|---|
直接使用 innerHTML 產生大量元素 |
會導致 重排 (reflow) 與 重繪 (repaint),效能下降 | 使用 DocumentFragment 或 批次 append,一次寫入多筆節點 |
| 忘記移除事件監聽器 | 記憶體泄漏、意外觸發舊事件 | 在 removeChild 或 element.remove() 前,先呼叫 removeEventListener,或使用 事件委派 |
在迴圈中一次一次 appendChild |
每次插入都會觸發 DOM 更新,效能不佳 | 建立 DocumentFragment,把所有子節點先加入 fragment,最後一次性插入 |
使用 appendChild 插入已存在於 DOM 的節點 |
會把節點從原位置搬移,可能造成 UI 突變 | 確認節點是否已在目標容器,必要時先 cloneNode(true) |
對 null 呼叫 removeChild |
產生 TypeError |
先檢查 parentNode && childNode 再執行 |
最佳實踐
- 批次操作:使用
DocumentFragment或append(...nodes)來減少重排。 - 保持語意:插入元素時,選擇最符合語意的 HTML 標籤(如
<ul>/<li>、<section>),有助於 SEO 與可讀性。 - 使用
classList管理樣式:避免直接寫入className,以免覆蓋既有類別。 - 避免過度依賴
innerHTML:若僅需插入文字,使用textContent;若需要插入大量 HTML,先建立元素再插入。 - 清理資源:在元素被移除前,手動解除與之相關的 計時器、WebSocket、Observer 等,確保不留下隱藏的資源。
實際應用場景
| 場景 | 需求 | 建議的實作方式 |
|---|---|---|
| 動態表單 | 使用者點擊「新增欄位」產生更多輸入框 | 使用 createElement('input') + appendChild(),同時為每個欄位綁定 input 事件。 |
| 無限捲動 (Infinite Scroll) | 捲動到底部時自動載入新文章 | 依照 API 回傳的資料,用 DocumentFragment 批次建立 <article>,最後一次 appendChild(fragment)。 |
| 即時聊天 | 收到新訊息即時顯示在聊天視窗 | append() 新的訊息 <div>,並在插入後呼叫 scrollIntoView(),確保畫面自動滾動。 |
| 可關閉的通知條 | 點擊「X」即移除通知 | 透過事件委派監聽 .close,使用 element.remove() 移除整個通知容器。 |
| 圖片懶載入 (Lazy Load) | 當圖片進入視窗時才載入 | 觀察者 (IntersectionObserver) 觸發時,用 setAttribute('src', ...),若需要替換佔位圖則使用 replaceWith()。 |
總結
在前端開發裡,新增與移除 DOM 元素 是最常見也最基礎的操作。透過本文的概念說明、五個實作範例、常見陷阱與最佳實踐,我們可以:
- 快速建立、安全插入、有效移除 任意元素;
- 避免效能瓶頸(如重排、記憶體泄漏);
- 在真實專案(表單、無限捲動、即時聊天等)中靈活運用。
只要遵循 語意化、批次處理、資源清理 的原則,便能寫出乾淨、可維護且效能優秀的前端程式碼。祝你在 JavaScript 的 DOM 操作之路上,玩得開心、寫得順手!