nav标签不定义层级,子菜单语义靠ul>li>ul嵌套结构实现;需用aria属性标识展开状态与当前页,并避免在nav内混入非导航元素。

nav 标签本身不定义层级,它只声明“这是一个导航区块”;子菜单的语义化靠内部 HTML 结构实现,核心是用嵌套的 ul > li > ul 模式表达父子关系,而非靠 nav 嵌套。
子菜单必须用嵌套列表结构
HTML5 规范明确支持多级导航通过列表嵌套表达。父级菜单项(li)内直接包裹一个新 ul,即表示其下存在子菜单。这种结构天然传达“包含关系”,屏幕阅读器会按层级播报,键盘用户也能自然 tab 进入子项。
- ✅ 正确:
<li>@#@#@#@#@#@#@#@#@#@0<ul> <li>@#@#@#@#@#@#@#@#@#@1</li> <li>@#@#@#@#@#@#@#@#@#@2</li> </ul> </li> - ❌ 错误:用
div或section替代嵌套ul—— 丢失语义层级,读屏器无法识别为子菜单 - ⚠️ 注意:子菜单
ul不需要额外nav包裹;整个多级结构仍属于同一个导航意图,共用一个<nav aria-label="主导航"></nav>
可访问性需同步处理展开状态
视觉上隐藏的子菜单(如 hover 或点击展开),必须通过 ARIA 属性向辅助技术传达当前状态,否则键盘用户无法感知是否可交互。
- 触发按钮(如箭头图标或“+”)要带
aria-expanded="false",展开后改为"true" - 该按钮需有
aria-controls="submenu-id",指向对应子菜单ul的 id - 子菜单
ul应设aria-hidden="true"初始状态,展开时改为"false" - 不要用
hidden属性——它会完全移除元素于可访问树之外
当前页高亮与焦点管理
层级再深,也要确保用户清楚“我在哪”以及“下一步去哪”。这依赖两个关键标记:
- 用
aria-current="page"标在当前页面对应的链接上(如@#@#@#@#@#@#@#@#@#@3),读屏器会读作“产品中心,当前页面” - 子菜单展开后,焦点应自动移到第一个子项链接;关闭时,焦点应回到触发它的父级链接或按钮
- 避免仅靠 CSS
:focus-visible实现,需 JS 主动调用.focus()确保行为可靠
避免常见语义污染
复杂菜单容易混入非导航元素,破坏 nav 的纯粹性:
- 搜索框、登录按钮、语言切换器等操作型控件,不应放在
nav内部——它们不是“跳转目标”,而是功能入口 - 如果子菜单里含图标、徽标或描述文字,确保这些内容用
span或small包裹,并加aria-hidden="true"(若纯装饰)或role="presentation" - 不要为每个子菜单单独加
nav标签——这会让辅助技术误判为多个独立导航区,实际只是同一导航下的分支











