什麼是 DOM?

當瀏覽器載入 HTML 檔案時,會將其解析成一個樹狀結構,稱為 DOM 樹。每個 HTML 標籤(如 <div><p><button>)都會轉換為一個節點(Node),這些節點之間存在父子兄弟的層級關係。透過這個樹狀結構,JavaScript 可以:

理解 DOM 是成為優秀前端開發者的必備技能。無論是建立簡單的表單驗證,還是開發複雜的單頁應用程式(SPA),DOM 操作都是不可或缺的基礎。

元素選取器

在操作 DOM 之前,我們需要先選取要操作的元素。JavaScript 提供了多種選取元素的方法,以下是最常用的幾種:

getElementById

getElementById 是最直接的元素選取方式,透過元素的 id 屬性來取得單一元素。由於 HTML 中 id 應該是唯一的,因此這個方法會回傳一個元素物件,或在找不到時回傳 null

// 選取 id 為 "title" 的元素
const title = document.getElementById('title');

// 檢查元素是否存在
if (title) {
    title.textContent = '新標題';
}

querySelector 與 querySelectorAll

querySelector 讓您使用 CSS 選擇器語法來選取元素,這提供了極大的彈性。querySelector 回傳第一個符合的元素,而 querySelectorAll 回傳一個 NodeList(節點列表),包含所有符合條件的元素。

// 選取第一個 class 為 "card" 的元素
const card = document.querySelector('.card');

// 選取所有 li 元素
const listItems = document.querySelectorAll('li');

// 選取特定的子元素
const submitBtn = document.querySelector('form button[type="submit"]');

// 遍歷所有選取的元素
listItems.forEach((item, index) => {
    item.textContent = `項目 ${index + 1}`;
});

其他常用的選取方法

方法 說明 回傳類型
getElementsByClassName 透過類別名稱選取 HTMLCollection
getElementsByTagName 透過標籤名稱選取 HTMLCollection
getElementsByName 透過 name 屬性選取 NodeList
效能建議:如果已知元素的 id,優先使用 getElementById,因為它是原生方法中效能最好的。若需要複雜的選擇邏輯,querySelectorquerySelectorAll 是最靈活的選擇。

事件處理

網頁的互動性來自於事件(Events)的處理。當使用者點擊按鈕、輸入文字、或移動滑鼠時,瀏覽器會產生對應的事件。透過 addEventListener 方法,我們可以註冊事件監聽器來回應這些操作。

addEventListener 基本用法

const button = document.querySelector('#myButton');

// 註冊點擊事件監聽器
button.addEventListener('click', function(event) {
    console.log('按鈕被點擊了!');
});

// 使用箭頭函式
button.addEventListener('click', (e) => {
    e.target.style.backgroundColor = 'var(--accent)';
});

常見的事件類型

事件類型 說明 適用元素
click 元素被點擊 所有可點擊元素
mouseenter/mouseleave 滑鼠進入/離開元素 幾乎所有元素
keydown/keyup 鍵盤按鍵按下/釋放 文件、輸入框
submit 表單提交 form 元素
input 輸入值變化 input, textarea
change 元素值變更確認 select, input
load 資源載入完成 window, img, script

事件委託(Event Delegation)

事件委託是一種高效的事件處理模式。與其為每個子元素個別監聽事件,不如將監聽器附加到父元素上,利用事件冒泡(Event Bubbling)的特性來處理。這在動態生成的元素上特別有用。

// 為父容器註冊單一監聽器,處理所有子按鈕的點擊
const container = document.querySelector('#buttonContainer');

container.addEventListener('click', (e) => {
    // 檢查點擊的目標是否是按鈕
    if (e.target.classList.contains('btn')) {
        console.log(' clicked:', e.target.textContent);
    }
});

事件物件(Event Object)

當事件觸發時,事件處理函式會接收一個事件物件作為參數。這個物件包含了豐富的資訊,讓我們能夠了解事件的詳細情況並做出適當的回應。

常見的事件物件屬性

document.addEventListener('click', (e) => {
    console.log('事件類型:', e.type);
    console.log('目標元素:', e.target.tagName);
    console.log('游標位置:', e.clientX, e.clientY);
});

// 阻止連結的預設跳轉行為
const link = document.querySelector('#externalLink');
link.addEventListener('click', (e) => {
    e.preventDefault();
    console.log('連結跳轉已阻止');
});

動態建立與修改元素

JavaScript 可以讓我們在網頁載入後動態地新增、修改或刪除元素。這是建立互動式應用的基礎,讓頁面能夠根據使用者的操作即時更新內容。

建立新元素

使用 createElement 方法可以建立新的 HTML 元素。這個方法接受標籤名稱作為參數,回傳一個新的元素節點。

// 建立新的 div 元素
const newDiv = document.createElement('div');
newDiv.className = 'card';
newDiv.textContent = '這是新建立的元素';

// 建立包含 HTML 內容的元素
const article = document.createElement('article');
article.innerHTML = `
    <h3>文章標題</h3>
    <p>文章內容...</p>
`;

新增元素到 DOM

建立元素後,需要將它加入到 DOM 樹中才會顯示。appendChild 會將元素加入父元素的最後一個子節點之後,而 insertBefore 則可以在特定位置插入元素。

// 假設有一個容器
const container = document.querySelector('#container');

// 建立新元素
const newItem = document.createElement('li');
newItem.textContent = '新項目';

// 加入到容器
container.appendChild(newItem);

// 在第一個子元素之前插入
const firstChild = container.firstElementChild;
if (firstChild) {
    container.insertBefore(newItem, firstChild);
}

刪除元素

刪除元素有兩種主要方式:使用父元素的 removeChild 方法,或直接呼叫元素本身的 remove 方法。

// 方式一:使用 removeChild
const parent = document.querySelector('#list');
const toRemove = document.querySelector('.item');
parent.removeChild(toRemove);

// 方式二:直接呼叫 remove(現代瀏覽器)
toRemove.remove();

// 清除所有子元素
while (container.firstChild) {
    container.removeChild(container.firstChild);
}

修改元素內容與屬性

// 修改文字內容
element.textContent = '新文字';

// 修改 HTML 內容(小心 XSS 攻擊)
element.innerHTML = '<strong>粗體文字</strong>';

// 修改屬性
image.src = 'new-image.jpg';
image.alt = '新圖片描述';

// 操作類別
element.classList.add('active');
element.classList.remove('hidden');
element.classList.toggle('expanded');
element.classList.contains('active'); // 回傳 true/false

// 修改行內樣式
element.style.color = '#ff0000';
element.style.display = 'none';

表單處理

表單是網頁與使用者互動的重要媒介。JavaScript 提供了豐富的 API 來處理表單輸入、表單驗證,以及提交事件。

取得表單資料

// 透過表單元素的名稱取得輸入值
const form = document.querySelector('#contactForm');

// 監聽表單提交事件
form.addEventListener('submit', (e) => {
    e.preventDefault(); // 阻止表單實際提交
    
    // 取得表單資料
    const formData = new FormData(form);
    const name = formData.get('name');
    const email = formData.get('email');
    
    console.log('姓名:', name);
    console.log('電子郵件:', email);
});

即時輸入驗證

// 即時監聽輸入事件
const emailInput = document.querySelector('#email');
const errorMsg = document.querySelector('#emailError');

emailInput.addEventListener('input', () => {
    const email = emailInput.value;
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    
    if (!emailRegex.test(email)) {
        errorMsg.textContent = '請輸入有效的電子郵件地址';
        emailInput.classList.add('error');
    } else {
        errorMsg.textContent = '';
        emailInput.classList.remove('error');
    }
});

常用表單事件

實用練習題

練習一:待辦事項清單

建立一個簡單的待辦事項應用程式,功能包括:

  1. 輸入新項目並按下 Enter 或點擊按鈕新增到清單
  2. 點擊項目可標記為已完成(刪除線效果)
  3. 點擊刪除按鈕可移除該項目
  4. 顯示目前項目總數

提示:使用 createElement 建立新項目,addEventListener 監聽點擊和輸入事件,appendChild 加入新元素,remove 刪除元素。

練習二:圖片點擊計數器

製作一個點擊計數器,功能包括:

  1. 點擊圖片時顯示「已點擊 N 次」
  2. 滑鼠移到圖片上時放大圖片
  3. 滑鼠離開時恢復原始大小

提示:利用事件物件的 target 屬性操作圖片,使用 style.transform 改變大小。

練習三:表單驗證系統

建立包含以下欄位的表單驗證:

  1. 使用者名稱(至少 3 個字元)
  2. 電子郵件(符合 Email 格式)
  3. 密碼(至少 8 個字元,包含數字和字母)
  4. 密碼確認(需與密碼相同)

提示:使用正規表達式驗證格式,在 submit 事件中檢查所有欄位,若驗證失敗呼叫 preventDefault 阻止提交。

最佳實踐建議

深入學習建議:掌握 DOM 操作的基礎後,建議繼續學習 Modern DOM API(如 MutationObserverIntersectionObserver)以及前端框架(如 React、Vue)的元件化開發方式,它們在 DOM 操作之上提供了更高效的抽象層。