纯HTML多级下拉应优先用<details><summary>,语义正确、天然支持键盘和屏幕阅读器;嵌套需直接子元素结构,IE不支持需降级;移动端必须用JS控制展开与ARIA状态同步。

用 <details> 实现纯 HTML 多级下拉(无 JS、语义正确)
桌面和移动端都能开合,且天然支持嵌套,比手写 <ul><li> + CSS :hover 更可靠。关键不是“怎么动”,而是“怎么被键盘和屏幕阅读器识别”——<details> 的 open 属性自带焦点管理、回车展开、语音播报状态,IE 除外。
-
<summary>必须是<details>的第一个子元素,不能加role="button"或onclick,它本身就是可聚焦控件 - 子菜单要嵌套,就直接在
<summary>后面放另一个<details>,不要包<div> - IE 全系不支持,若需兼容,得降级为
<ul><li class="has-submenu">+ JS 切换类
<ul><li> 嵌套结构必须满足的三个硬性条件
很多“菜单不动”或“Tab 键跳过子项”的问题,根源不在 CSS,而在 HTML 结构本身不合法。浏览器只对标准嵌套有完整支持。
- 子菜单
<ul>必须是父级<li>的**直接子元素**,不能平级、不能外置、不能用<div>包一层 - 顶级
<ul>应加role="menubar",每个可展开的<li>加aria-haspopup="true"和aria-expanded="false" - 子菜单
<ul>必须加role="menu",其下每个<li>加role="menuitem",否则屏幕阅读器读不出层级
移动端点击展开必须靠 JS 切换类,:hover 在真机上基本失效
iOS Safari 和 Android Chrome 对 :hover 的响应极不可靠,尤其在缩放、手势操作或快速滑动后。纯 CSS 悬停方案等于默认关闭手机端功能。
- 用 JS 监听
click,给父<li>添加submenu-open类,再通过.submenu-open > ul控制显隐 - 每次展开前先
querySelectorAll(".submenu-open").forEach(el => el.classList.remove("submenu-open")),避免多开 - 同步更新
aria-expanded值:el.setAttribute("aria-expanded", "true"),否则辅助技术无法感知状态变化
CSS 定位闪退、子菜单错位的常见原因
鼠标移出就关闭、子菜单飘到屏幕外、缩放后位置偏移——这些问题几乎都出在热区断连或定位计算误差上,跟动画时长无关。
立即学习“前端免费学习笔记(深入)”;
- 绝对定位子菜单时,
top: 100%后别加margin-top: 1px,这 1px 就是闪退元凶 - 推荐用父
<li>加padding-bottom: 1px扩展热区,让鼠标从父项滑向子菜单时不经过空白间隙 - 用
visibility: hidden而非display: none隐藏子菜单,否则键盘用户按 Tab 会直接跳过整个区域



















