nav元素必须直接位于body下,不可嵌套在header或footer中;须用aria-label明确标识用途,内部只含真实href链接或导航控件,禁用空链接和非导航内容。

nav 元素必须包裹在 <body> 内,且不能嵌套在 <header> 或 <footer> 里重复语义
HTML5 的 <nav> 是独立的语义化区块,代表页面主要导航链接集合。它不是装饰性容器,浏览器和读屏工具会据此识别导航意图。常见错误是把它当“样式盒子”用——比如套在 <header> 里再加一层 <div class="nav-wrapper">,结果语义被稀释,AT(辅助技术)可能跳过或误判。
正确做法:<nav> 应直接位于 <body> 下(或作为 <main> / <article> 的兄弟元素),内部只放链接、列表或带导航功能的交互控件:
<body>
<header><h1>站点标题</h1></header>
<nav aria-label="主导航">
<ul>
<li><a href="/home">首页</a></li>
<li><a href="/about">关于</a></li>
</ul>
</nav>
<main>...</main>
</body>
多个 nav 区域需用 aria-label 或 aria-labelledby 明确区分用途
一个页面可能出现多组导航:顶部主菜单、侧边栏分类、页脚快捷链接、面包屑。若不标注,读屏软件无法区分“这是页脚导航还是内容内跳转”,用户可能反复进入同一类导航。
必须为每个 <nav> 提供唯一可访问标识:
立即学习“前端免费学习笔记(深入)”;
-
<nav aria-label="主导航">—— 用于顶部水平菜单 -
<nav aria-label="页脚资源链接">—— 避免写成 "Footer navigation" -
<nav aria-labelledby="bc-title"><h2 id="bc-title">当前位置</h2>...</nav>—— 面包屑用标题关联更自然
不要依赖 CSS 类名或位置推断用途;aria-label 值应简短、具体、中文优先(如“文章分类导航”,而非“nav-2”)。
响应式折叠菜单中,<nav> 本身不负责控制显隐,状态要靠 aria-expanded 和 aria-controls 同步
移动端常把导航收进汉堡按钮,但很多人只用 JS 切换 display: none,却忘了同步 ARIA 状态。这会导致键盘用户按 Tab 进入隐藏区域,或读屏读不到“菜单已展开”提示。
关键配合点:
- 按钮需有
aria-expanded="false",点击后切换为"true" - 按钮需有
aria-controls="nav-menu-id",指向对应<nav id="nav-menu-id"> -
<nav>不加hidden属性(它会完全移出可访问树),改用aria-hidden="true"并随状态更新 - 展开时确保焦点进入第一个链接,关闭时焦点回退到触发按钮
纯 CSS 实现的折叠(如 :checked + .nav)也需同样注入 ARIA 属性,否则对键盘和读屏无效。
nav 内部链接应避免空 href 或 javascript:void(0)
导航区域本质是跳转路径集合。出现 <a href="#"> 或 <a href="javascript:void(0)"> 会让读屏报“链接无目标”,键盘用户按 Enter 会刷新/跳顶,破坏预期行为。
真实场景处理方式:
- 下拉菜单项如果是纯触发(如展开子菜单),改用
<button type="button">,并配aria-haspopup="true"和aria-expanded - 尚未上线的入口,用
<span role="link" tabindex="0">+ 自定义事件,同时加aria-disabled="true"和视觉灰度 - 锚点链接必须确保目标存在且可聚焦(如
href="#section2"对应<section id="section2">)
检查方法:禁用 JavaScript 后手动点击所有 <a>,看是否仍能合理跳转或给出明确反馈。
nav 的边界很清晰:它只管“去哪”,不管“怎么去”。任何依赖 JS 才能完成跳转的设计,都得先保证语义和基础可用性兜底。



















