nav 导航
<nav> 元素表示页面的导航区域,包含一组导航链接。它是 HTML5 中最重要的语义化标签之一,让屏幕阅读器用户能够快速跳转到站点的不同部分,也帮助搜索引擎理解站点的链接结构。
前置知识
阅读本节前,建议先了解:header 页眉
基础概念
什么是 nav
<nav> 用于包裹页面中主要的导航链接集合。一个页面可以有多个 <nav> 元素,例如全局导航、面包屑导航、侧边栏导航等。浏览器和辅助技术会将 <nav> 识别为导航 landmark,提供快速导航功能。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>nav 导航示例</title>
</head>
<body>
<!-- 全局主导航 -->
<nav aria-label="主导航">
<ul>
<li><a href="/">首页</a></li>
<li><a href="/about">关于</a></li>
<li><a href="/blog">博客</a></li>
<li><a href="/contact">联系</a></li>
</ul>
</nav>
<!-- 页面主内容 -->
<main>
<article>
<h1>文章标题</h1>
<p>文章内容...</p>
</article>
</main>
<!-- 页脚导航 -->
<footer>
<nav aria-label="页脚链接">
<a href="/privacy">隐私政策</a>
<a href="/terms">使用条款</a>
</nav>
</footer>
</body>
</html>nav 的隐式 ARIA 角色
| 属性 | 值 | 说明 |
|---|---|---|
| 隐式角色 | navigation | 自动映射为导航 landmark |
| 可访问名称 | 无(需要 aria-label 或 aria-labelledby) | 区分页面中多个 nav |
nav 的使用原则
<nav> 并不是用来包裹所有链接的。只有当一组链接构成主要导航区块时才使用 <nav>:
- 应该使用
<nav>:主导航、面包屑、目录、分页导航 - 不应使用
<nav>:页脚的法律链接、文章内引用链接、广告链接、标签云
语法与使用
基本语法
html
<nav [aria-label="描述"] [aria-labelledby="id"]>
<!-- 导航链接列表 -->
<ul>
<li><a href="/link1">链接1</a></li>
<li><a href="/link2">链接2</a></li>
</ul>
</nav>nav 的内容模型
<nav> 内部可以包含任何流内容,但最常见的模式是使用列表:
html
<!-- 模式 1:无序列表(最推荐) -->
<nav aria-label="主导航">
<ul>
<li><a href="/">首页</a></li>
<li><a href="/about">关于</a></li>
</ul>
</nav>
<!-- 模式 2:直接链接 -->
<nav aria-label="快速链接">
<a href="#section1">第一节</a>
<a href="#section2">第二节</a>
<a href="#section3">第三节</a>
</nav>
<!-- 模式 3:带分组的链接 -->
<nav aria-label="分类导航">
<details>
<summary>前端开发</summary>
<ul>
<li><a href="/html">HTML</a></li>
<li><a href="/css">CSS</a></li>
<li><a href="/js">JavaScript</a></li>
</ul>
</details>
</nav>详细说明
主导航 vs 辅助导航
页面中通常有以下类型的导航:
| 导航类型 | 位置 | aria-label 示例 | 是否必须 |
|---|---|---|---|
| 全局导航 | header 中 | "主导航" | 推荐 |
| 面包屑 | 内容区顶部 | "面包屑" | 推荐 |
| 章节目录 | aside 中 | "目录" | 可选 |
| 页脚链接 | footer 中 | "页脚导航" | 推荐 |
| 分页导航 | 列表底部 | "文章分页" | 可选 |
| 上下篇导航 | 文章底部 | "文章导航" | 可选 |
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>多导航示例</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
/* 页面头部 */
.site-header {
background: #2d3748;
color: #fff;
padding: 1rem 2rem;
}
.header-inner {
display: flex;
align-items: center;
justify-content: space-between;
max-width: 1200px;
margin: 0 auto;
}
.site-name { font-size: 1.25rem; font-weight: 700; }
/* 全局导航样式 */
.global-nav ul {
list-style: none;
display: flex;
gap: 1.5rem;
}
.global-nav a {
color: #cbd5e0;
text-decoration: none;
font-size: 0.9rem;
transition: color 0.2s;
}
.global-nav a:hover { color: #fff; }
/* 面包屑样式 */
.breadcrumb {
max-width: 1200px;
margin: 1rem auto;
padding: 0 2rem;
}
.breadcrumb ol {
list-style: none;
display: flex;
gap: 0.5rem;
font-size: 0.85rem;
color: #718096;
}
.breadcrumb li + li::before {
content: ">";
margin-right: 0.5rem;
color: #a0aec0;
}
.breadcrumb a {
color: #4299e1;
text-decoration: none;
}
/* 内容区布局 */
.content-layout {
max-width: 1200px;
margin: 1rem auto;
padding: 0 2rem;
display: grid;
grid-template-columns: 1fr 250px;
gap: 2rem;
}
/* 侧边目录 */
.toc { background: #f7fafc; padding: 1rem; border-radius: 8px; }
.toc h2 { font-size: 1rem; margin-bottom: 0.75rem; color: #2d3748; }
.toc ul { list-style: none; }
.toc li { margin-bottom: 0.5rem; }
.toc a { color: #4a5568; text-decoration: none; font-size: 0.85rem; }
.toc a:hover { color: #4299e1; }
/* 文章导航(上下篇) */
.post-nav {
max-width: 1200px;
margin: 2rem auto;
padding: 0 2rem;
display: flex;
justify-content: space-between;
}
.post-nav a {
color: #4299e1;
text-decoration: none;
}
/* 页脚 */
.site-footer {
background: #2d3748;
color: #a0aec0;
text-align: center;
padding: 1.5rem 2rem;
margin-top: 2rem;
}
.footer-nav {
margin-bottom: 0.75rem;
}
.footer-nav a {
color: #cbd5e0;
text-decoration: none;
margin: 0 0.5rem;
font-size: 0.85rem;
}
</style>
</head>
<body>
<!-- 全局导航(主导航) -->
<header class="site-header">
<div class="header-inner">
<div class="site-name">TechBlog</div>
<nav class="global-nav" aria-label="主导航">
<ul>
<li><a href="/">首页</a></li>
<li><a href="/tutorials">教程</a></li>
<li><a href="/articles">文章</a></li>
<li><a href="/tools">工具</a></li>
<li><a href="/about">关于</a></li>
</ul>
</nav>
</div>
</header>
<!-- 面包屑导航 -->
<nav class="breadcrumb" aria-label="面包屑">
<ol itemscope itemtype="https://schema.org/BreadcrumbList">
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<a itemprop="item" href="/"><span itemprop="name">首页</span></a>
<meta itemprop="position" content="1">
</li>
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<a itemprop="item" href="/tutorials"><span itemprop="name">教程</span></a>
<meta itemprop="position" content="2">
</li>
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<a itemprop="item" href="/tutorials/html"><span itemprop="name">HTML</span></a>
<meta itemprop="position" content="3">
</li>
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<span itemprop="name">语义化标签</span>
<meta itemprop="position" content="4">
</li>
</ol>
</nav>
<!-- 主内容区 -->
<div class="content-layout">
<main>
<article>
<h1>HTML5 语义化标签教程</h1>
<p>本文详细介绍了 HTML5 语义化标签的使用方法...</p>
</article>
<!-- 文章上下篇导航 -->
<nav class="post-nav" aria-label="文章导航">
<a href="/prev-article">← 上一篇:HTML 表单基础</a>
<a href="/next-article">下一篇:CSS Grid 布局 →</a>
</nav>
</main>
<!-- 侧边目录导航 -->
<aside>
<nav class="toc" aria-label="文章目录">
<h2>目录</h2>
<ul>
<li><a href="#section1">什么是语义化</a></li>
<li><a href="#section2">语义化标签介绍</a></li>
<li><a href="#section3">最佳实践</a></li>
<li><a href="#section4">常见问题</a></li>
</ul>
</nav>
</aside>
</div>
<!-- 页脚导航 -->
<footer class="site-footer">
<nav class="footer-nav" aria-label="页脚导航">
<a href="/privacy">隐私政策</a>
<a href="/terms">使用条款</a>
<a href="/sitemap">网站地图</a>
<a href="/rss">RSS 订阅</a>
</nav>
<p>© 2024 TechBlog. All rights reserved.</p>
</footer>
</body>
</html>aria-label 区分多个 nav
当页面有多个 <nav> 时,必须使用 aria-label 或 aria-labelledby 来区分它们,否则屏幕阅读器用户无法区分不同的导航区域:
html
<!-- 错误:多个 nav 没有 aria-label,屏幕阅读器只能读到 "navigation" -->
<nav>
<ul><li><a href="/">首页</a></li></ul>
</nav>
<nav>
<ul><li><a href="/privacy">隐私政策</a></li></ul>
</nav>
<!-- 正确:每个 nav 都有描述性的 aria-label -->
<nav aria-label="主导航">
<ul><li><a href="/">首页</a></li></ul>
</nav>
<nav aria-label="页脚导航">
<ul><li><a href="/privacy">隐私政策</a></li></ul>
</nav>
<!-- 也可以使用 aria-labelledby 引用可见的标题 -->
<nav aria-labelledby="toc-title">
<h2 id="toc-title">目录</h2>
<ul>
<li><a href="#s1">第一节</a></li>
</ul>
</nav>何时使用 nav
使用 <nav> 的判断标准:
适合使用 <nav> 的场景:
html
<!-- 1. 主导航 -->
<nav aria-label="主导航">...</nav>
<!-- 2. 面包屑 -->
<nav aria-label="面包屑">...</nav>
<!-- 3. 目录/索引 -->
<nav aria-label="文章目录">...</nav>
<!-- 4. 分页 -->
<nav aria-label="分页">
<a href="/page/1">1</a>
<a href="/page/2">2</a>
<a href="/page/3">3</a>
</nav>
<!-- 5. 上下文导航(上一篇/下一篇) -->
<nav aria-label="文章导航">...</nav>
<!-- 6. 语言切换 -->
<nav aria-label="语言选择">
<a href="/en">English</a>
<a href="/zh">中文</a>
</nav>不适合使用 <nav> 的场景:
html
<!-- 1. 文章内的引用链接(太多 nav 会干扰导航) -->
<p>更多信息请参考 <a href="https://example.com">MDN 文档</a>。</p>
<!-- 2. 页脚的少量法律链接 -->
<footer>
<p>
<a href="/privacy">隐私政策</a> |
<a href="/terms">使用条款</a>
</p>
</footer>
<!-- 3. 社交媒体图标链接 -->
<div>
<a href="#" aria-label="Twitter"><img src="twitter.svg" alt=""></a>
<a href="#" aria-label="GitHub"><img src="github.svg" alt=""></a>
</div>
<!-- 4. 标签列表 -->
<div>
<a href="/tag/html">HTML</a>
<a href="/tag/css">CSS</a>
</div>实战示例
电子商务网站导航
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>电商网站导航示例</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
/* 顶部工具栏 */
.top-bar {
background: #1a202c;
color: #a0aec0;
font-size: 0.8rem;
padding: 0.4rem 2rem;
}
.top-bar-inner {
max-width: 1200px;
margin: 0 auto;
display: flex;
justify-content: space-between;
}
.top-bar nav a {
color: #a0aec0;
text-decoration: none;
margin-left: 1rem;
}
.top-bar nav a:hover { color: #fff; }
/* 主 header */
.main-header {
background: #2b6cb0;
color: #fff;
padding: 1rem 2rem;
}
.main-header-inner {
max-width: 1200px;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: space-between;
}
.brand {
display: flex;
align-items: center;
gap: 0.5rem;
}
.brand-name { font-size: 1.5rem; font-weight: 700; }
/* 分类导航 */
.category-nav {
background: #2c5282;
}
.category-nav-inner {
max-width: 1200px;
margin: 0 auto;
padding: 0 2rem;
}
.category-nav ul {
list-style: none;
display: flex;
gap: 0;
}
.category-nav li {
position: relative;
}
.category-nav a {
display: block;
color: #bee3f8;
text-decoration: none;
padding: 0.75rem 1.25rem;
font-size: 0.9rem;
transition: background 0.2s;
}
.category-nav a:hover {
background: rgba(255,255,255,0.1);
color: #fff;
}
/* 面包屑 */
.breadcrumb {
max-width: 1200px;
margin: 1rem auto;
padding: 0 2rem;
}
.breadcrumb ol {
list-style: none;
display: flex;
gap: 0.5rem;
font-size: 0.85rem;
color: #718096;
}
.breadcrumb li + li::before {
content: ">";
margin-right: 0.5rem;
}
.breadcrumb a { color: #3182ce; text-decoration: none; }
/* 分页 */
.pagination {
max-width: 1200px;
margin: 2rem auto;
padding: 0 2rem;
text-align: center;
}
.pagination ul {
list-style: none;
display: inline-flex;
gap: 0.25rem;
}
.pagination a, .pagination span {
display: inline-block;
padding: 0.5rem 1rem;
border: 1px solid #e2e8f0;
border-radius: 4px;
text-decoration: none;
color: #4a5568;
font-size: 0.9rem;
}
.pagination a:hover { background: #ebf8ff; border-color: #bee3f8; }
.pagination .current {
background: #3182ce;
color: #fff;
border-color: #3182ce;
}
</style>
</head>
<body>
<!-- 顶部工具栏导航 -->
<div class="top-bar">
<div class="top-bar-inner">
<span>欢迎来到 ShopMall</span>
<nav aria-label="账户导航">
<a href="/login">登录</a>
<a href="/register">注册</a>
<a href="/orders">我的订单</a>
<a href="/wishlist">收藏夹</a>
</nav>
</div>
</div>
<!-- 主头部 + 搜索导航 -->
<header class="main-header">
<div class="main-header-inner">
<div class="brand">
<span class="brand-name">ShopMall</span>
</div>
<nav aria-label="用户操作导航">
<a href="/cart">购物车(3)</a>
<a href="/help">帮助中心</a>
</nav>
</div>
</header>
<!-- 商品分类导航 -->
<nav class="category-nav" aria-label="商品分类">
<div class="category-nav-inner">
<ul>
<li><a href="/electronics">数码电子</a></li>
<li><a href="/clothing">服装鞋帽</a></li>
<li><a href="/home">家居生活</a></li>
<li><a href="/books">图书音像</a></li>
<li><a href="/food">食品生鲜</a></li>
<li><a href="/beauty">美妆护肤</a></li>
</ul>
</div>
</nav>
<!-- 面包屑导航 -->
<nav class="breadcrumb" aria-label="面包屑">
<ol>
<li><a href="/">首页</a></li>
<li><a href="/electronics">数码电子</a></li>
<li><a href="/phones">手机通讯</a></li>
<li><span>智能手机</span></li>
</ol>
</nav>
<main>
<p>商品列表内容...</p>
<!-- 分页导航 -->
<nav class="pagination" aria-label="商品列表分页">
<ul>
<li><span class="current" aria-current="page">1</span></li>
<li><a href="/phones?page=2">2</a></li>
<li><a href="/phones?page=3">3</a></li>
<li><a href="/phones?page=4">4</a></li>
<li><a href="/phones?page=2">下一页 →</a></li>
</ul>
</nav>
</main>
</body>
</html>注意事项
常见错误
- 所有链接都用 nav 包裹:
<nav>只用于主要导航区块,不是包裹链接的通用容器 - 缺少 aria-label:多个 nav 没有标签时,屏幕阅读器无法区分
- nav 内没有链接:
<nav>应该包含导航链接,空 nav 没有意义 - 过度嵌套 nav:一个导航列表内不需要再嵌套另一个 nav
html
<!-- 错误:nav 内嵌套 nav(通常不合理) -->
<nav aria-label="主导航">
<nav aria-label="子导航">
<a href="#">链接</a>
</nav>
</nav>
<!-- 如果确实需要分组,使用 section 或直接列表 -->
<nav aria-label="主导航">
<section>
<h2>前端</h2>
<ul>
<li><a href="/html">HTML</a></li>
<li><a href="/css">CSS</a></li>
</ul>
</section>
</nav>最佳实践
- 始终为 nav 添加 aria-label:即使是唯一的 nav 也建议添加,因为将来可能会增加更多 nav
- 使用列表结构:
<ul>/<li>是导航最自然的结构,屏幕阅读器能播报"列表,共 N 项" - 指示当前位置:使用
aria-current="page"标记当前页面 - 面包屑使用 ol:面包屑是有序的,应该用
<ol>而非<ul> - 分页使用 aria-label:帮助屏幕阅读器用户理解分页的目的
下一节
继续学习:main 主体内容