必须使用<nav>标签,因其是HTML5中唯一被规范定义为“导航链接集合”的元素,屏幕阅读器可识别为可跳转地标;其他标签如<div>或<header>无法替代其语义功能。

必须用 <nav>,不是 <div class="header-nav">,也不是 <header> —— 否则屏幕阅读器根本不知道这是导航。
为什么 <nav> 不能被其他标签替代
后台系统里大量存在 <div class="menu"> 或把整个页头塞进 <header> 的写法。这两种都错:<div> 没语义,辅助技术只读作“一个区域”;<header> 的语义是“页眉”,即使加了 role="navigation",原生语义仍优先,读出来还是“页眉”,不是“导航”。
只有 <nav> 是 HTML5 中唯一被规范定义为“导航链接集合”的元素,浏览器和屏幕阅读器会主动将其识别为可跳转地标,并在快捷键(如 NVDA 的 D 键)中单独列出。
- 多个
<nav>必须配aria-label,比如<nav aria-label="主导航">和<nav aria-label="系统设置菜单"> -
<nav>内部不能再嵌套<nav>,否则可访问性树断裂,内层可能被忽略 - 哪怕菜单在左侧、折叠状态、或由 JS 动态渲染,只要功能是“跳转到不同模块”,就必须用
<nav>
<nav> 里的列表结构怎么写才对
用 <ul> + <li> + <a> 是标准且最稳妥的组合。它天然支持键盘 Tab 导航、语义层级清晰、无脚本时仍可点击,也方便后续用 CSS 控制布局(flex/column)或 JS 绑定事件。
立即学习“前端免费学习笔记(深入)”;
别用 <div> 堆链接,也别省略 <li> 直接把 <a> 放进 <ul> —— 这违反 HTML 规范,某些旧版读屏会跳过或误读。
- 激活项必须加
aria-current="page",不能只靠class="active" - 禁用菜单项要设
aria-disabled="true"并移除tabindex,否则键盘用户能聚焦却无法触发 - 下拉菜单的触发按钮需有
aria-expanded和aria-controls,且值要与对应下拉容器的id匹配
滚动时固定/隐藏顶部导航的 DOM 结构前提
Bootstrap 的 data-spy="affix" 或现代 JS 实现(如 IntersectionObserver)都依赖正确的 DOM 层级。如果 <nav> 被包在无语义的 <div> 里,或和 logo、搜索框混在一个 <header> 中,JS 就很难精准控制它的定位行为,更别说做无障碍适配。
正确结构是:把纯导航链接抽出来,独立成 <nav aria-label="主导航">,放在 <header> 内部或紧邻位置 —— 顺序不重要,但语义隔离必须干净。
- 不要让
<nav>包含非导航内容(如用户头像、搜索框),它们该属于<header>或独立容器 - 滚动逻辑只作用于
<nav>元素本身,避免父容器 transform 或 overflow 隐藏导致 fixed 失效 - 移动端折叠菜单的
<button>必须用aria-controls指向目标<div class="collapse">,且该<div>不应放在<nav>外部
重构顶部导航最容易被忽略的点,不是样式或交互,而是语义隔离是否彻底 —— 一个 <nav> 里混进 logo、搜索、用户信息,或者用 <header> 顶替 <nav>,都会让可访问性检查直接失败,且后续所有 JS 行为都可能偏离预期。



















