原生 <details> 最简实现手风琴菜单,但 IE 不支持、旧 Edge 功能受限;多开默认,单开需 JS 监听 toggle 事件控制 open 属性;CSS 自定义用 ::marker(Safari 15.4+)或 [open] 选择器;框架中推荐状态管理而非依赖原生属性。

手风琴菜单用 <details> 最简单,但兼容性要注意
现代浏览器直接用原生 <details> + <summary> 就能实现无 JS 的手风琴效果,不用写一行 JavaScript。但 IE 完全不支持,Edge 12–18 仅部分支持(无动画、open 属性不可控),如果必须兼容旧版 Edge 或 IE,就得换方案。
示例结构:
<details> <summary>标题内容</summary> <p>折叠区正文</p> </details>
-
<summary>点击后自动展开/收起,无需绑定事件 - 默认关闭;加
open属性可默认展开:<details open> - 多个
<details>默认互不影响(即多开),如需“单开”(手风琴式互斥),必须用 JS 控制
要“单开”就得用 JS 控制 open 属性
原生 <details> 不自带互斥逻辑,点一个开、另一个不会自动关。得监听 toggle 事件,手动关掉其他项。
关键操作:
立即学习“前端免费学习笔记(深入)”;
- 给所有
<details>加统一 class,比如accordion-item - 监听每个的
toggle事件(注意:不是click,因为键盘空格/回车也能触发) - 在事件回调里遍历兄弟节点,对非当前项调用
.removeAttribute('open')
简短示例:
document.querySelectorAll('.accordion-item').forEach(item => {
item.addEventListener('toggle', () => {
document.querySelectorAll('.accordion-item').forEach(other => {
if (other !== item && other.hasAttribute('open')) {
other.removeAttribute('open');
}
});
});
});
用 CSS 自定义样式时,<summary> 的 ::marker 很关键
默认箭头丑、难对齐,且各浏览器渲染差异大。用 ::marker 能统一控制符号,但 Safari 15.4+ 才支持,旧版 Safari 和 Chrome 需降级处理。
- 隐藏默认 marker:
summary::marker { content: ''; } - 用伪元素或背景图加自定义图标:
summary::before { content: '▶'; } - 展开状态判断靠
details[open] summary::before改符号,比如变成 ▼ - 注意:Firefox 不支持
summary::before,得用details[open] summary配合背景图或内联 SVG
React/Vue 项目里别硬套 <details>,状态管理更可靠
框架组件生命周期和 DOM 同步机制会让 <details open> 行为不稳定,尤其在条件渲染或 key 变化时,open 属性可能被忽略或滞后更新。
- React 推荐用 state 控制显隐,配合
useRef操作 DOM 的open属性作同步(非必需,纯 CSS 切换也行) - Vue 中避免直接写
:open="isActive",应改用v-show或高度过渡动画,更可控 - 框架里真正省事的是用现成库(如
@headlessui/react的Disclosure),它内部已处理好焦点、动画、键盘支持
手风琴最易被忽略的不是展开逻辑,而是键盘可访问性——<summary> 天然支持空格/回车切换,但手动 JS 实现时容易漏掉 onkeydown 处理,导致残障用户无法操作。



















