為什麼 HTML 的問題最難發現

JavaScript 錯了會噴 console error;CSS 錯了畫面明顯壞掉。HTML 的問題通常靜默發生——頁面看起來正常,但螢幕閱讀器失效、爬蟲抓錯、CSS 選擇器衝突,問題要到很後期才浮現。


Anti-pattern 1:div 濫用——每個東西都包一層

<!-- ❌ 不必要的 div 包裝 -->
<div class="title-wrapper">
  <div class="title-inner">
    <h1>頁面標題</h1>
  </div>
</div>
 
<!-- ✅ 標題就是標題 -->
<h1>頁面標題</h1>

每多一層 div,CSS 選擇器就多一層、DOM 就多一個節點、JavaScript 遍歷就多一步。遇到需要包裝的情況先問:這個 wrapper 是語意需要,還是 CSS 習慣?

如果只是為了讓 flexbox 生效,很多時候直接在父元素加 CSS 就解了。


Anti-pattern 2:重複 id

<!-- ❌ id 在整個頁面必須唯一 -->
<div id="card">商品 A</div>
<div id="card">商品 B</div>
<div id="card">商品 C</div>

id 的定義是頁面唯一識別符。重複 id 的後果:

  • document.getElementById('card') 只回傳第一個
  • CSS #card 選擇器理論上只應該命中一個,但瀏覽器行為不一致
  • <label for="...">aria-labelledby="..." 指向重複 id 會失效
  • W3C Validator 會報錯,部分爬蟲會困惑

動態產生的卡片、列表項目改用 class,或加 index(id="card-1"id="card-2")。


Anti-pattern 3:不必要的巢狀

<!-- ❌ inline 元素包 block 元素(invalid HTML) -->
<span>
  <div>這個 div 在 span 裡</div>
</span>
 
<!-- ❌ p 包 div(invalid HTML,瀏覽器會自動截斷 p) -->
<p>
  說明文字
  <div class="note">補充</div>
  繼續說明
</p>
 
<!-- ❌ a 包 a(invalid HTML) -->
<a href="/outer">
  外層連結
  <a href="/inner">內層連結</a>  <!-- 瀏覽器會截斷 -->
</a>

這些 invalid 巢狀不會報錯,但瀏覽器的自動修復行為不一致,會造成奇怪的 CSS / JS bug。用 W3C Validator 跑一遍就能抓到。


Anti-pattern 4:tabindex 正數

<!-- ❌ 用正數 tabindex 強制改 tab 順序 -->
<input tabindex="3" name="last-name">
<input tabindex="1" name="first-name">
<button tabindex="2">送出</button>

正數 tabindex 會打亂鍵盤使用者和螢幕閱讀器的預期流程,維護時改了 HTML 順序還要同步改 tabindex 數字,很快變成地雷。

正確做法:讓 DOM 順序就是正確的 tab 順序。需要用到的只有:

  • tabindex="0":讓非互動元素加入 tab 順序(自訂元件時)
  • tabindex="-1":讓元素可以 .focus() 但不在 tab 順序裡(modal 聚焦管理)

Anti-pattern 5:onclick 加在非互動元素

<!-- ❌ div / span 綁 click 事件,鍵盤用不了 -->
<div onclick="doSomething()">點我執行</div>
<span onclick="openMenu()">選單</span>
 
<!-- ✅ 用 button,天生可以 tab、Enter/Space 觸發 -->
<button onclick="doSomething()">點我執行</button>
<button onclick="openMenu()">選單</button>

<div> 的 click 事件滑鼠可以觸發,但鍵盤使用者 tab 不到(<div> 預設不可聚焦),Enter 也不會觸發。如果一定要用 div,要補 tabindex="0"keydown 監聽 Enter 和 Space——工作量是用 <button> 的五倍,還不如一開始就用對元素。


Anti-pattern 6:alt 屬性誤用

<!-- ❌ 資訊圖片沒有 alt -->
<img src="revenue-chart.png">
 
<!-- ❌ 裝飾圖片有不必要的 alt,螢幕閱讀器會朗讀 -->
<img src="divider.svg" alt="分隔線裝飾">
 
<!-- ❌ alt 直接填檔名 -->
<img src="IMG_0042.jpg" alt="IMG_0042">
 
<!-- ✅ 資訊圖片:描述圖片傳達的資訊 -->
<img src="revenue-chart.png" alt="2026 Q1 營收較去年同期成長 23%">
 
<!-- ✅ 裝飾圖片:alt="" 讓螢幕閱讀器完全跳過 -->
<img src="divider.svg" alt="">
 
<!-- ✅ 圖示按鈕:描述功能,不是圖示外觀 -->
<button><img src="search-icon.svg" alt="搜尋"></button>

alt 的原則:如果圖片消失,使用者需要知道什麼才不會失去資訊? 那就是 alt 要寫的內容。裝飾圖片消失使用者不需要知道任何事,所以 alt=""


快速工具清單

問題工具
invalid HTML、重複 id、非法巢狀W3C Validator
alt 缺失、ARIA 問題、tab orderaxe DevTools(Chrome 擴充套件)
全面 a11y 稽核Chrome DevTools → Lighthouse → Accessibility
程式碼靜態分析HTMLHint(可加進 CI)

工具細節見 HTML 檢查工具


相關文章