JavaScript – DOM 與瀏覽器 API
單元:文件選取(querySelector / getElementById)
簡介
在前端開發中,DOM(Document Object Model) 是連結 HTML 文件與 JavaScript 程式碼的橋樑。只有正確、快速地取得想操作的元素,才能實現動態 UI、表單驗證、動畫效果等各種互動功能。querySelector 與 getElementById 是最常使用的兩種選取方法,前者支援 CSS 選擇器,彈性極高;後者則只針對 id,在已知唯一標識時速度最快。了解兩者的差異、使用時機與最佳實踐,對於從 初學者 逐步成長為 中階開發者 相當關鍵。
本篇文章將從概念說明、實作範例、常見陷阱以及實務應用等面向,完整介紹這兩個 API,讓你在開發中能夠 快速定位元素、降低錯誤、提升效能。
核心概念
1. getElementById – 直接以 ID 取元素
- 語法:
document.getElementById(id) - 傳回值:若找到相符的元素,回傳 單一 HTMLElement;找不到則回傳
null。 - 特性:
- 只接受 字串 型別的
id。 - 因為 HTML 中的
id必須唯一,瀏覽器在內部使用 哈希表(hash table)快速定位,效能最佳。 - 不支援 CSS 選擇器語法(例如
.class、[type="text"])。
- 只接受 字串 型別的
小提醒:
id必須符合 HTML 規範(不能以數字開頭、不能有空白),否則getElementById仍會回傳元素,但會影響 CSS 與其他 API 的行為。
2. querySelector – 用 CSS 選擇器選取單一元素
- 語法:
document.querySelector(selector) - 傳回值:符合 第一個(最左側)匹配的元素;找不到則回傳
null。 - 特性:
- 接受任意合法的 CSS 選擇器(ID、class、屬性、階層、偽類…)。
- 只會回傳 第一筆匹配結果,若想取得全部符合的元素,需使用
querySelectorAll。 - 在大型文件中,瀏覽器會先執行 selector engine(如 Sizzle)解析,效能稍遜於
getElementById,但差距在現代瀏覽器已相當小。
3. 為什麼同時提供兩種 API?
- 彈性 vs 效能:
querySelector為開發者提供了類似 CSS 的寫法,讓代碼更具可讀性;getElementById則在已知唯一 ID 時提供最快的存取。 - 語意清晰:使用
getElementById能明確表達「我只需要這個唯一元素」,有助於程式碼自說明(self‑documenting)。 - 相容性:
getElementById自 IE4 起就支援;querySelector於 IE8+(IE9+ 完整支援)才加入,現在已是所有主流瀏覽器的標準。
程式碼範例
以下示範 5 個實用案例,說明兩者在不同情境下的使用方式與注意點。
範例 1️⃣:最基本的 ID 取得(getElementById)
<!-- index.html -->
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<title>getElementById 範例</title>
</head>
<body>
<h1 id="title">Hello World</h1>
<script>
// 直接取得 id 為 title 的 <h1> 元素
const titleEl = document.getElementById('title');
// 改變文字內容
titleEl.textContent = '你好,JavaScript!';
</script>
</body>
</html>
重點:
getElementById回傳的就是<h1>,不需要再做型別斷言(TypeScript 中會自動推斷為HTMLElement)。
範例 2️⃣:使用 CSS 選擇器取得第一個符合的元素(querySelector)
<div class="card">
<p class="desc">第一段描述</p>
</div>
<div class="card">
<p class="desc">第二段描述</p>
</div>
<script>
// 只會拿到第一個 .card 裡面的 .desc
const firstDesc = document.querySelector('.card .desc');
console.log(firstDesc.textContent); // => "第一段描述"
</script>
說明:
.card .desc是「在.card內部的.desc」的階層選擇器,querySelector只回傳第一筆匹配結果。
範例 3️⃣:結合 querySelector 與屬性選擇器
<input type="text" name="username" placeholder="使用者名稱">
<input type="password" name="password" placeholder="密碼">
<script>
// 取得 name 為 password 的 <input>
const pwdInput = document.querySelector('input[name="password"]');
// 設定預設值(僅示範,實務上不建議明文寫入)
pwdInput.value = '123456';
</script>
技巧:屬性選擇器可以讓你不必依賴
id或class,在表單大量相似元素時特別有用。
範例 4️⃣:使用 getElementById 取得表單元素並監聽事件
<form id="loginForm">
<input id="user" type="text" placeholder="帳號">
<input id="pwd" type="password" placeholder="密碼">
<button type="submit">登入</button>
</form>
<script>
const form = document.getElementById('loginForm');
const userInput = document.getElementById('user');
const pwdInput = document.getElementById('pwd');
form.addEventListener('submit', function (e) {
e.preventDefault(); // 防止表單真的送出
alert(`帳號:${userInput.value}\n密碼:${pwdInput.value}`);
});
</script>
最佳實踐:表單內的每個欄位都使用唯一
id,配合getElementById,讓程式碼在閱讀時一眼就能看出「哪個欄位」在被操作。
範例 5️⃣:動態產生元素後再使用 querySelector 取得
<div id="container"></div>
<script>
// 動態建立一個 <ul>,裡面有三個 <li>
const ul = document.createElement('ul');
ul.innerHTML = `
<li class="item">項目 A</li>
<li class="item">項目 B</li>
<li class="item">項目 C</li>
`;
document.getElementById('container').appendChild(ul);
// 立即使用 querySelector 取得第一個 .item
const firstItem = document.querySelector('#container .item');
firstItem.style.color = 'tomato';
</script>
要點:即使元素是 動態插入 的,只要在插入完成之後呼叫
querySelector,仍能正確取得。若在插入前呼叫,會得到null。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方案/最佳實踐 |
|---|---|---|
| 1. 使用錯誤的選擇器語法 | getElementById('#myId') 會因為多了 # 而找不到元素。 |
getElementById('myId')(不加 #) |
| 2. 多個相同 ID | HTML 允許重複 ID,會導致 getElementById 只回傳第一個,其他元素無法被選取。 |
確保 全站唯一,若需要多個相同標記,改用 class 或 data- 屬性。 |
3. 過度使用 querySelectorAll 再取第一筆 |
有時開發者寫 document.querySelectorAll('.item')[0],其實可以直接用 querySelector('.item'),省去陣列建立與索引的開銷。 |
若只需要第一筆,直接使用 querySelector。 |
| 4. 在元素未載入前就執行選取 | 若腳本放在 <head>,DOM 尚未完成解析,getElementById / querySelector 會回傳 null。 |
把腳本放在 body 結尾,或使用 DOMContentLoaded 事件:document.addEventListener('DOMContentLoaded', () => { /* 選取程式 */ }); |
| 5. 依賴過於複雜的 CSS 選擇器 | 過長的選擇器(如 div > ul > li > a.active) 會降低效能,且在結構變動時易斷裂。 |
盡量使用 簡潔且具意義的 class / id,必要時加上 data- 屬性作為選取依據。 |
效能小技巧
- 優先使用
getElementById:在已知唯一 ID 時,直接使用getElementById,效能約比querySelector('#id')高 2–3 倍。 - 避免過度遍歷:若需要在大量元素中搜尋,先限定範圍(如
section.querySelector)再進行搜尋,可減少整份文件的遍歷成本。 - 快取選取結果:同一元素會被多次使用時,先存入變數,避免重複呼叫 API。
實際應用場景
| 場景 | 建議使用的選取方式 | 為什麼 |
|---|---|---|
| 表單驗證 | getElementById 針對每個欄位的 id |
每個欄位唯一,易於維護且效能佳。 |
| 動態列表(如新聞、商品) | querySelectorAll('.list-item') 配合 forEach |
需要一次取得全部項目,並逐一處理。 |
| 單頁應用(SPA)路由切換 | querySelector('#app') + querySelector('.view.active') |
只需定位根容器與目前顯示的視圖。 |
| 模態視窗/彈出層 | document.querySelector('[data-modal="login"]') |
使用 data- 屬性避免 id 重複,且語意清晰。 |
| CSS 動畫觸發 | document.querySelector('.animate-target') |
透過 class 選擇器快速取得目標,配合 classList.add() 觸發動畫。 |
總結
getElementById:最快、最直接的單一元素取得方式,適用於 唯一 ID 的情境。querySelector:彈性十足的 CSS 選擇器,能一次解決多種選取需求,但在大量元素或頻繁呼叫時需留意效能。- 最佳實踐:
- 先思考 元素的唯一性,若唯一則優先
getElementById。 - 若需要階層、屬性或多條件篩選,使用
querySelector(或querySelectorAll)。 - 確保腳本執行時 DOM 已完成解析,避免
null。 - 盡量使用 語意清晰的 class / data-屬性 來作為選取依據,提升可讀性與維護性。
- 先思考 元素的唯一性,若唯一則優先
掌握這兩個核心 API,你就能在 DOM 操作 上游刃有餘,無論是簡單的表單互動或是複雜的單頁應用,都能以最直觀、最有效率的方式取得目標元素。祝你寫程式寫得開心,專案順利上線! 🚀