details标签默认不支持多级嵌套折叠,原生无手风琴逻辑;需通过css(如display:block、max-height过渡)和js(toggleattribute、监听toggle事件)配合实现稳定菜单效果,并注意ios safari的summary点击失效等兼容性问题。

details标签默认不支持多级嵌套折叠
直接用 <details></details> 做菜单,点开一个就会收起其他——这不是 bug,是规范行为。HTML 标准里 <details></details> 本身没有「手风琴」逻辑,open 属性互不影响,但浏览器渲染时若没加 CSS 控制,视觉上容易误以为“只能开一个”。
常见错误现象:click 事件监听了,toggle 也手动触发了,但多个 <details></details> 同时展开后,子菜单文字被截断、滚动错位,或者父级 <summary></summary> 点击没反应。
- 必须给
<details></details>加display: block(部分旧版 Safari 默认为inline) - 子
<details></details>要包裹在父<div> 或 <code><section></section>里,不能直接兄弟并列放在同一父容器下(否则<summary></summary>的点击区域易重叠) - 避免在
<summary></summary>里放<button></button>或<a></a>,会干扰原生 toggle 行为 - 用
details[open] > *:not(summary)选中展开后的内容区 - 配合
height: auto和overflow: hidden,再用 JS 测量真实高度后设为max-height(否则 transition 不生效) - 更稳妥的做法:只对
<summary></summary>加cursor: pointer和user-select: none,避免误触 - 优先用
details.toggleAttribute('open'),比赋值更可靠 - 监听
toggle事件而不是click,因为用户也可能用键盘空格键触发 - 如果菜单需保持单开(手风琴),得手动遍历其他
<details></details>并removeAttribute('open'),注意排除自身 - 不要在
toggle事件回调里再调details.open = false,会触发二次事件循环 - 给
<summary></summary>显式加position: relative和z-index: 1 - 避免对
<details></details>或其父元素使用transform: translateZ(0)或will-change: transform - 必要时用
ontouchstart事件手动触发details.toggleAttribute('open'),但仅限 iOS
用 CSS 控制 details 的展开/收起动画
<details></details> 原生不带动画,直接显示/隐藏很生硬。靠 max-height + transition 模拟,但要注意:内容高度不确定时,不能写死 max-height: 200px,否则文字多时溢出或少时留白。
实操建议:
JavaScript 手动控制 open 状态的陷阱
想用 JS 控制开关?别直接改 details.open = true 然后就完事。某些场景下(比如 Vue/React 动态渲染),DOM 还没挂载完成,open 属性会被框架忽略;或者用了 details.removeAttribute('open'),但样式没同步更新。
关键点:
移动端 Safari 的 summary 点击失效问题
iOS 15+ 上,<summary></summary> 在 flex 容器里、或父级有 transform 时,点击无响应——这是 WebKit 已知 bug,不是你代码写错了。
绕过方式:
细节折叠菜单看着简单,但跨浏览器表现差异集中在 <summary></summary> 的事件捕获、CSS 渲染层叠和 JS 生命周期上,最容易被忽略的是:没检查是否所有 <details></details> 都有唯一 id,导致用 JS 查找时批量误操作。











