html编辑器不提供多级导航菜单功能,必须手写嵌套结构、添加aria属性(aria-haspopup/expanded)并配合css定位或checkbox方案实现;否则将导致语义缺失、键盘不可访问、屏幕阅读器失效。

直接说结论:HTML 编辑器本身不提供“创建多级导航菜单”的功能,它只是写代码的工具;真正决定菜单是否多级、能否展开、是否可访问的,是你手写的 <ul></ul> 嵌套结构 + 对应的 CSS 选择器逻辑 + 必要的 ARIA 属性。用所谓“可视化拖拽编辑器”生成的多级菜单,90% 会破坏语义、丢失键盘导航、在屏幕阅读器下完全不可读。
为什么不能依赖 HTML 编辑器的“菜单生成器”
多数所见即所得(WYSIWYG)编辑器(如 Dreamweaver 旧版、某些 CMS 内置编辑器)的“插入菜单”功能,本质是把菜单项塞进一堆 <div> 和 <code><span></span> 里,再靠 JS 绑定 click 事件控制显隐。这导致:
- DOM 结构扁平,二级菜单不是一级
<li>的子元素,而是同级<div> ——<code>:hover或:checked选择器根本选不到 - 缺失
role="menu"、aria-haspopup="menu"、aria-expanded等必要属性,NVDA/JAWS 朗读时跳过整个子菜单 - Tab 键无法进入子菜单项,键盘用户卡死在第一层
- 移动端 touch 事件无 fallback,点击后立即收起(因为没维持状态)
必须手写的核心 HTML 结构
所有可靠多级菜单的起点,只有一种写法:嵌套 <ul></ul>,且子 <ul></ul> 必须是父 <li> 的**直接子元素**。例如:
<nav><ul class="main-menu">
<li><a href="/">首页</a></li>
<li class="has-submenu">
<a href="/services" aria-haspopup="menu" aria-expanded="false">服务</a>
<ul class="submenu">
<li><a href="/web-design">网页设计</a></li>
<li class="has-submenu">
<a href="/dev" aria-haspopup="menu" aria-expanded="false">开发</a>
<ul class="submenu">
<li><a href="/frontend">前端</a></li>
<li><a href="/backend">后端</a></li>
</ul>
</li>
</ul>
</li>
</ul></nav>
关键点:
- 每个可展开的
<a></a>必须带aria-haspopup="menu"和初始aria-expanded="false" - 子
<ul></ul>必须紧跟在父<li>内,不能被<div> 包裹,也不能靠 JS 动态插入 <li>不要用 <code>id或class模拟层级关系(如menu-level-2),浏览器和辅助技术不认这个 -
Hover + position:仅适用于桌面端,二级菜单用
position: absolute定位,父<li>必须设position: relative;三级菜单用left: 100%向右展开,避免脱离文档流导致悬停失效 -
Checkbox + label:用隐藏
<input type="checkbox">绑定<label></label>,通过:checked ~ .submenu控制显示;好处是状态可保持、无需 JS、SSR 友好;但注意~是通用兄弟选择器,.submenu必须紧跟在<label></label>后面,中间不能插其他元素 - 没给每级
<li>加position: relative→ 三级菜单绝对定位错位,悬停区域与视觉位置不重合 - 子菜单用
margin-left缩进 → 焦点框(outline)偏移,键盘用户看不到当前聚焦项 - 响应式断点没加
@media (max-width: 768px) { .submenu { display: none !important; } }→ 移动端子菜单强行展开,遮盖正文,且无收起入口
CSS 控制展开的两种可靠路径
纯 CSS 实现展开,只有两类能兼顾语义、无障碍和移动端基础体验:
别用 display: none 配合 :hover 做三级菜单——鼠标移向子菜单时,因脱离父 <li> 区域,:hover 状态瞬间丢失,菜单闪退。
容易被忽略的兼容性硬伤
即使结构和 CSS 都写对了,以下三点不做,菜单在真实场景中仍会失败:
最麻烦的不是写不出来,而是写出来后在 iPhone 上点一下就收、在 Chromebook 上 Tab 键进不去第二层、在 NVDA 下读作“列表,1 项”——这些都不是样式问题,是 DOM 层级和 ARIA 属性没对齐。











