💡 文章重點摘要:在日常專案開發與 Code Review 中,時常看到新手甚至部分中階工程師患上嚴重的「
<div>症候群(Divitis)」——忽略 HTML5 語意化標籤的價值,從頂部導覽列、側邊目錄到底部分頁器,清一色用<div class="nav-bar">堆疊到底。雖然在一般螢幕上看長得一模一樣,但在搜尋引擎爬蟲與視障使用者的無障礙樹(Accessibility Tree)眼中,兩者卻有天壤之別!本文將帶你從底層地標角色(Landmark Roles)映射機制切入,掌握 WHATWG 官方對<nav>的使用邊界決策樹、aria-current="page"狀態綁定,以及 Safari 下list-style: none抹殺列表語意等 4 大生產環境避坑實戰!
⚡ 快速簡單使用(30 秒複製即用範本)
如果你想立刻將網站的主導覽列與麵包屑升級為符合 W3C 與 SEO 標準的 <nav> 語意化標籤結構(含多導覽列 aria-label 區分、aria-current="page" 當前頁標記與 Safari 列表語意防禦),可直接複製下方範本:
<!-- 1. 全站主選單:同頁有多個 <nav> 時,必須以 aria-label 標註唯一名稱 -->
<nav aria-label="全站主選單">
<!-- Safari 防坑:當 CSS 設定 list-style: none 時,顯式加上 role="list" 保住清單語意 -->
<ul class="nav-list" role="list">
<li><a href="/" aria-current="page">首頁</a></li>
<li><a href="/tutorials">技術文章</a></li>
<li><a href="/about">關於站長</a></li>
</ul>
</nav>
<!-- 2. 麵包屑導覽:使用有序列表 <ol> 表達層級順序 -->
<nav aria-label="麵包屑路徑">
<ol class="breadcrumb-list" role="list">
<li><a href="/">首頁</a></li>
<li><a href="/html">HTML 專欄</a></li>
<li><span aria-current="page">語意化標籤指南</span></li>
</ol>
</nav>
💡 3 秒修改重點:
- 一個頁面若有 2 個以上的
<nav>,務必加上aria-label(不要包含「導覽」二字以免螢幕閱讀器重複播報,例如寫aria-label="主選單")。- 當前所在頁面的連結請加上
aria-current="page",讓螢幕閱讀器直接播報「目前頁面」。
一、 為什麼視覺長一樣,底層卻天差地遠?DOM 樹與無障礙樹雙軌揭秘
許多初入前端領域的新手常問:「<nav> 跟 <div> 在瀏覽器預設樣式裡都只是 display: block 的區塊元素,為什麼不能直接寫 <div class="nav"> 就好?」
答案在於:現代瀏覽器在解析 HTML 時,除了建立供 CSS 渲染畫面使用的 DOM 樹(Document Object Model Tree)之外,還會在底層同步建立一棵專門提供給輔助科技(如螢幕閱讀器 NVDA、JAWS、VoiceOver)與搜尋引擎理解頁面結構的 無障礙樹(Accessibility Tree / AOM)!
<div>的本質:純粹的通用排版容器(Generic Container),在 WAI-ARIA 規範中對應role="generic",在無障礙樹與爬蟲眼中完全沒有任何結構地標意義。<nav>的本質:HTML5 區段語意化標籤(Sectioning Content),瀏覽器出廠即自動賦予它隱含的role="navigation"地標角色(Landmark Role)。

當視障使用者進入你的網站時,他們不會從頭到尾聽完幾百個字,而是直接按下螢幕閱讀器的地標快捷鍵(例如在 NVDA / JAWS 按下 D 鍵切換不同 Landmark 地標,或按下 N 鍵跳過整串導覽連結直達非連結內文;在 macOS VoiceOver 則透過 Rotor 轉盤切換地標)。如果你的導覽列是用 <div> 寫的,這群使用者將完全找不到網站選單,也無法一鍵跳過冗長的頂部連結!
二、 5 秒快速索引:nav vs div 與核心地標標籤速查表
在切版選型時,請直接對照這張 HTML5 結構語意標籤與 <div> 的職責對照表:
| 標籤名稱 | 隱含 ARIA 角色 (Implicit Role) | 單頁建議數量 | 核心語意職責與最佳使用時機 | 生產環境關鍵注意事項 |
|---|---|---|---|---|
<nav> | role="navigation" | 1 ~ 多個 | 包覆主要導覽連結群組(主選單、麵包屑、文章目錄 TOC、分頁器)。 | 若同頁出現 2 個以上 <nav>,必須加上 aria-label 或 aria-labelledby 區分名稱。 |
<div> | role="generic" | 不限 | 零語意的純視覺佈局、Flex/Grid 容器、樣式包裝器(Wrapper)。 | 找不到任何符合語意的原生標籤時,才退而求其次使用 <div>。 |
<header> | role="banner"(位於 <body> 頂層時) | 全頁頂層 1 個 (區段內不限) | 網站頂部橫幅(含 Logo、搜尋框、主選單)或 <article> 的標題資訊區。 | 若包在 <article> 或 <section> 內,會失去全站 banner 地標身分,變為一般區段標頭。 |
<main> | role="main" | 全頁唯一 1 個 (顯示狀態下) | 當前頁面獨一無二的核心主內容區(排除全站共用的頂欄、側欄與頁尾)。 | 搭配頂部「跳至主要內容(Skip Link)」錨點(href="#main-content"),大幅提升鍵盤體驗。 |
<aside> | role="complementary" | 1 ~ 多個 | 與主內容間接相關的附屬區塊(側邊推薦欄、廣告欄、作者簡介卡)。 | 切勿把全站主導覽列直接塞在 <aside> 而不包 <nav>。 |
<footer> | role="contentinfo"(位於 <body> 頂層時) | 全頁頂層 1 個 (區段內不限) | 頁尾版權宣告、隱私權條款、聯絡資訊與次要網站地圖(Sitemap)連結。 | 頁尾簡短的「版權與服務條款」連結直接放 <footer> 即可,不需額外硬包 <nav>。 |
三、 什麼時候該用 nav?WHATWG 官方邊界決策樹
新手最常犯的兩個極端錯誤,一是「全站一個 <nav> 都不用」,二是**「矯枉過正,只要看到 <a> 超連結就通通拿 <nav> 包起來」**!
根據 WHATWG HTML Living Standard 4.3.4 規範,<nav> 僅適用於「主要導覽區塊(Major Navigation Blocks)」。為了讓你在切版時 3 秒內做出正確判斷,請遵循以下決策樹:

四、 生產級實戰:重構「萬年 div 導覽列」與 aria-current 最佳實踐
讓我們將一個典型的「非語意化 div 頂部導覽列」,升級為符合現代 WCAG 無障礙標準與 SEO 規範的生產級程式碼。
1. ❌ 舊版反模式(Divitis 反面教材)
<!-- ❌ 錯誤示範:全用 div 堆疊、靠 .active class 變色卻無語意、缺少跳過導覽連結 -->
<div class="header">
<div class="logo"><a href="/">TechBlog</a></div>
<div class="nav-bar">
<div class="nav-item active"><a href="/articles">技術文章</a></div>
<div class="nav-item"><a href="/tutorials">實戰教學</a></div>
<div class="nav-item"><a href="/about">關於站長</a></div>
</div>
</div>
<div class="main-body">
<h1>最新技術文章</h1>
</div>
這段程式碼在生產環境的 3 大缺陷:
- 鍵盤使用者每次換頁,都必須狂按數十次
Tab鍵穿過頂部選單,才能抵達正文的按鈕。 - 視障使用者完全不知道目前所在的頁面是「技術文章」,因為
.active只改變了視覺顏色,無障礙樹對此一無所知。 - 爬蟲與閱讀器無法識別
.nav-bar是導覽地標。
2. 🟢 現代語意化生產級重構(含 Skip Link + aria-current="page")
<!-- 🟢 最佳實踐:結合 Skip Link、header、nav、role="list" 與 aria-current -->
<header class="site-header">
<!-- 1. 無障礙逃生門 (Skip Link):平時隱藏,按下 Tab 鍵時浮現,一鍵跳過導覽列直達正文 -->
<a href="#main-content" class="skip-link">跳至主要內容</a>
<div class="brand-logo">
<a href="/">TechBlog</a>
</div>
<!-- 2. 明確標註 aria-label,與頁面底部的分頁導覽或麵包屑做出區隔 -->
<nav class="primary-nav" aria-label="全站主選單">
<!-- 3. 顯式加上 role="list",防止 Safari 因 CSS list-style: none 抹殺列表語意 -->
<ul class="nav-list" role="list">
<li>
<!-- 4. 使用 aria-current="page" 宣告當前頁面,並直接作為 CSS 樣式選擇器! -->
<a href="/articles" aria-current="page">技術文章</a>
</li>
<li><a href="/tutorials">實戰教學</a></li>
<li><a href="/about">關於站長</a></li>
</ul>
</nav>
</header>
<!-- 5. 對應 Skip Link 的唯一 main 地標容器,設定 tabindex="-1" 確保程式化錨點聚焦穩定 -->
<main id="main-content" tabindex="-1">
<h1>最新技術文章</h1>
</main>
/* 1. Skip Link 樣式:預設移出畫面外,僅在鍵盤 Tab 聚焦 (:focus-visible) 時優雅浮現 */
.skip-link {
position: absolute;
top: -100px;
left: 16px;
z-index: 9999;
padding: 8px 16px;
background-color: #0f172a;
color: #ffffff;
font-weight: 600;
border-radius: 6px;
text-decoration: none;
transition: top 0.2s ease;
}
.skip-link:focus-visible {
top: 12px;
outline: 3px solid #38bdf8;
outline-offset: 2px;
}
/* 2. 導覽清單重置:配合 HTML 上的 role="list" 確保跨瀏覽器語意不丟失 */
.nav-list {
list-style: none;
margin: 0;
padding: 0;
display: flex;
gap: 24px;
}
/* 3. 狀態驅動 CSS:直接綁定 [aria-current="page"],徹底淘汰無語意的 .active class! */
.nav-list a {
color: #475569;
text-decoration: none;
font-weight: 500;
padding: 6px 0;
}
.nav-list a[aria-current="page"] {
color: #0284c7;
font-weight: 700;
border-bottom: 2px solid #0284c7;
}
Expected Output (預期執行結果)
- 鍵盤導航體驗:進入頁面按一下
Tab鍵,左上角立即浮現深色「跳至主要內容」按鈕;按下Enter後,焦點直接跳過整個主選單,精準落入<main id="main-content">。 - 螢幕閱讀器播報:進入選單時會清晰播報:「全站主選單,導覽地標,清單,共 3 個項目」;聚焦於第一個連結時會播報:「技術文章,目前頁面,連結」。
- CSS 與狀態 100% 同步:永遠不會再發生「改了
.active卻忘記改無障礙屬性」的脫節問題,因為視覺高亮直接由[aria-current="page"]驅動!
五、 生產環境常見的 4 大 nav 踩坑與排錯指南 (Pitfalls & Troubleshooting)
地雷 1:同頁多個 nav 忘記加 aria-label,引發無障礙地標「撞名」
- ❌ 錯誤現象:一個部落格文章頁同時有「頂部主選單
<nav>」、「麵包屑<nav>」、「文章右側目錄<nav>」與「底部上一篇/下一篇分頁<nav>」,全部都只寫光禿禿的<nav>。 - 💥 為什麼會壞:當視障使用者打開螢幕閱讀器的地標清單(Landmark List)時,會看到畫面上列出四個一模一樣的「導覽 (navigation)、導覽 (navigation)、導覽 (navigation)、導覽 (navigation)」,完全無法分辨哪個才是麵包屑或主選單!
- 解決思路:只要同頁有超過一個
<nav>,每一個都必須透過aria-label(或指向內部可見標題 ID 的aria-labelledby)賦予唯一且具描述性的名稱。- ⚠️ 重要細節:
aria-label的內容切勿包含「導覽」或「Navigation」字眼!例如不要寫aria-label="主選單導覽",否則螢幕閱讀器會重複唸出「主選單導覽,導覽」。直接寫aria-label="主選單"、aria-label="麵包屑路徑"、aria-label="文章章節分頁"即可。
- ⚠️ 重要細節:
地雷 2:在 CSS 寫了 list-style: none,結果 Safari 把 ul 的列表語意吃掉了!
- ❌ 踩坑現象:在 Chrome 和 Firefox 測試
<nav><ul><li>...</li></ul></nav>都很正常,為什麼在 macOS / iOS Safari 搭配 VoiceOver 測試時,有時卻聽不到「清單,共 N 個項目」的提示? - 💥 為什麼會壞:這是 Apple WebKit 引擎一個極為著名的設計決策。由於早年太多開發者濫用
<ul>加上list-style: none來做一般排版,WebKit 決定:只要<ul>或<ol>在 CSS 中被設定了list-style: none(或list-style-type: none),WebKit 就會在無障礙樹中移除它的隱含role="list"語意! - 解決思路:凡是在需要清除小圓點的導覽列表或商品列表上,養成習慣在 HTML 顯式補上
<ul role="list">,即可強制在 Safari / VoiceOver 下完整保留列表語意。
地雷 3:迷信「nav 裡面只能放 ul」,不敢放 ol 或 p
- ❌ 迷思:許多教學文章宣稱「
<nav>裡面規定只能寫<ul><li>」。 - 💥 真相還原:根據 WHATWG 官方規範,
<nav>內部的結構應該依據導覽連結本身的順序語意決定:- 無序並列的選單(如首頁、產品、關於我們):使用無序列表
<ul>。 - 具備嚴格先後順序的導覽(如「麵包屑 Breadcrumbs:首頁 > 分類 > 文章」或「多步驟結帳流程導覽」):必須使用有序列表
<ol>! - 簡短的句子型導覽:WHATWG 明確指出,在
<nav>內使用<p>段落包覆導覽連結也是完全合法的標準寫法。
- 無序並列的選單(如首頁、產品、關於我們):使用無序列表
地雷 4:把搜尋框 form 或行動版漢堡按鈕塞錯位置
- ❌ 錯誤寫法:把整個網站的
<header>內容(包含品牌 Logo、全站搜尋框、購物車彈窗按鈕)全部一股腦包進同一個<nav>裡面。 - 解決思路:
<header>才是頂部橫幅的總容器。Logo 與搜尋表單應直接放在<header>內,與<nav>保持兄弟節點(Sibling)關係;唯有控制選單展開/收合的漢堡按鈕(<button aria-expanded="false" aria-controls="nav-menu">),應放在<nav>內部作為第一個子元素,讓螢幕閱讀器使用者進入<nav>地標時能第一時間觸發按鈕展開選單!
六、 工程師上線前必備:HTML nav 與語意化導覽檢驗清單 (Checklist)
在切版完成或提交 Code Review 前,請對照以下 5 點清單進行自我查核:
- ☐ 地標精準度檢核:主選單、麵包屑、文章目錄(TOC)與分頁器皆已採用
<nav>包覆;頁尾簡短條款與文章內嵌連結未濫用<nav>。 - ☐ 多
<nav>唯一命名:頁面中若存在多個<nav>,皆已加上不重複的aria-label(且字串末尾未贅加「導覽 / Navigation」字詞)。 - ☐ 當前頁語意綁定:目前所在頁面的導覽連結已掛載
aria-current="page"(麵包屑最後一項亦同),並以[aria-current="page"]驅動高亮樣式。 - ☐ 有序與無序列表區分 & WebKit 防護:一般選單使用
<ul>、麵包屑與分頁順序使用<ol>;且凡設定list-style: none的列表皆已顯式補上role="list"。 - ☐ 鍵盤逃生門(Skip Link):
<header>頂部第一位已配置指向<main id="main-content" tabindex="-1">的「跳至主要內容」隱藏連結,且按Tab鍵時具備清晰的:focus-visible視覺焦點框。
七、 參考資料與延伸閱讀
- MDN Web Docs:
<nav>導覽區段元素 – Mozilla 官方對<nav>標籤的完整屬性說明、無障礙考量與麵包屑實作範例。 - WHATWG HTML Living Standard: The nav element – HTML 官方標準規格書對
<nav>使用邊界與非清單結構的權威定義。 - MDN Web Docs: aria-current 狀態屬性 – 深入理解如何利用
aria-current="page"與aria-current="step"標示當前位置。 - WebAIM: Semantic Structure – Regions, Headings, and Lists – 權威無障礙組織解析 HTML5 地標結構(Landmarks)與螢幕閱讀器導航行為。
發表迴響