不支持原生多级联动,需 js 监听 toggle 事件手动控制子项 open 状态;纯 css 可实现视觉嵌套与基础交互,但无法解决展开/收起行为同步问题。

details标签本身不支持原生多级嵌套展开逻辑
<details></details> 是语义化折叠控件,浏览器只保证单层 <summary></summary> 点击触发自身 <details></details> 的 open 状态切换。把一个 <details></details> 直接塞进另一个的内部(比如在子 <summary></summary> 里或 <details></details> 内容区)能渲染出来,但子项不会继承父项的展开/收起行为——父收起时子仍可能保持 open,且无事件联动。
常见错误现象:click 父 <summary></summary> 后子菜单没关闭;鼠标移入子项时意外触发父关闭;键盘导航(Tab + Enter)无法按层级跳转。
- 真正需要“菜单层级联动”,必须靠 JavaScript 手动监听
toggle事件并控制子<details></details>的open属性 - 若仅需视觉嵌套(如缩进、箭头对齐),纯 CSS 就够用,无需 JS
- 所有嵌套的
<details></details>必须有唯一id,否则 JS 控制时容易误操作
用CSS实现视觉嵌套和基础交互样式
浏览器默认的 <details></details> 样式简陋,子项常和父项挤在一起,看不出层级。用 CSS 可以加缩进、箭头、边框区分:
details > details {
margin-left: 1.5em;
}
details[open] > summary::after {
content: " ▼";
}
details:not([open]) > summary::after {
content: " ▶";
}
summary {
list-style: none;
outline: none;
}
summary:focus {
outline: 1px dotted #666;
}
注意:不要用 display: block 覆盖 <summary></summary> 默认样式,否则会破坏可访问性;用 margin 或 padding 调整间距更安全。
-
::after里的符号建议用 Unicode 字符(如 ▶ / ▼),避免图片或字体图标增加加载负担 - 如果项目要支持 RTL(从右向左排版),记得加
direction: rtl和对应翻转箭头 - 移动端点击热区小,给
summary加min-height和padding提升触控体验
用JavaScript手动同步嵌套的open状态
核心逻辑是:父 <details></details> 关闭时,递归关闭所有后代 <details></details>;父打开时,不自动打开子项(保留用户上次选择)。
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('details').forEach(el => {
el.addEventListener('toggle', () => {
if (!el.open) {
el.querySelectorAll('details').forEach(child => {
child.open = false;
});
}
});
});
});
这段代码只处理“父关→子关”,不干扰用户主动操作子项。如果要做“父开→子全开”,得额外加判断,但通常不推荐——违背用户预期,尤其当子项内容较多时。
- 避免用
querySelectorAll('details details'),它选的是所有后代,包括跨层级的;用el.querySelectorAll('details')(在 el 内部查)更精准 - IE 不支持
<details></details>,如果还要兼容,得 fallback 到aria-expanded+display: none模拟,但那就不是原生<details></details>了 - 如果菜单由 JS 动态生成,记得在插入 DOM 后再绑定
toggle事件,或用事件委托
键盘导航与可访问性不能只靠HTML自动补全
原生 <details></details> 支持 Tab 进入 <summary></summary>、Enter/Space 切换状态,但嵌套后焦点流容易混乱:用户 Tab 到父项,按 Enter 展开,再按 Tab 会跳到页面下一个可聚焦元素,而不是子 <summary></summary>。
修复方式只有两个:
- 用
tabindex="-1"和focus()手动控制焦点顺序(例如父展开后,自动focus()第一个子<summary></summary>) - 或者放弃纯
<details></details>,改用<button aria-expanded></button>+<div role="region"> 组合,完全自控键盘流 <p>别指望加个 <code>role="menu"就能变标准菜单——WAI-ARIA menu 模式要求严格的状态管理、方向键响应、Esc关闭等,比<details></details>复杂得多。简单导航菜单,老实用<details></details>+ 上面那段 JS 就行;真要复杂交互,直接上专门的菜单库更省心。











