是唯一原生支持折叠/展开且语义正确、键盘可访问的html元素,无需js即可实现;需确保为首个子元素,用open属性控制默认状态,样式定制应避开::marker而改用::before伪元素,互斥手风琴效果需js监听toggle事件处理。

用 <details></details> 和 <summary></summary> 实现最简折叠按钮
不用 JS 就能做出语义正确、键盘可访问的折叠按钮,<details></details> 是唯一原生支持该行为的 HTML 元素。它不是“按钮”,但行为完全符合点击切换展开/收起的需求。
常见错误是把 <summary></summary> 当普通 <button></button> 处理,加 onclick 或手动控制 display —— 这会破坏可访问性,且 Safari 下可能无法触发折叠逻辑。
-
<summary></summary>必须是<details></details>的第一个子元素,否则不响应点击 - 默认收起:直接写
<details><summary>标题</summary>内容</details> - 默认展开:加
open属性,即<details open>…</details> - 内容区域自动参与文档流,无需设
height: 0或overflow: hidden
<details></details> 的样式定制限制与绕过方法
原生 <summary></summary> 的视觉控制能力弱,尤其箭头图标和过渡动画不能靠纯 CSS 原生实现。强行用 summary::marker { content: "" } 在 Safari 中无效,这是最常踩的坑。
推荐做法是放弃依赖 ::marker,改用 ::before 自定义图标:
- 先隐藏默认箭头:
summary { list-style: none; } - 用
summary::before插入 Unicode 图标或 SVG,例如content: "▶"; - 展开时旋转:
details[open] summary::before { transform: rotate(90deg); transition: transform 0.2s; } - 不要给
<details></details>设height过渡 —— 它会压垮内部布局;若需高度动画,必须切到 JS +max-height方案
需要互斥手风琴效果?JS 不可避免
多个 <details></details> 默认各自独立,点一个不会关掉其他。要做“单开”效果(比如 FAQ 列表),必须监听 toggle 事件并手动关闭其余项。
关键点不是写逻辑,而是避免重复触发:
- 用
event.target判断哪个<details></details>被触发 - 遍历所有同级
<details></details>,对非当前项调用.removeAttribute('open') - 不要用
click事件替代toggle—— 后者在键盘空格/回车触发时也生效,click会漏掉 - 注意 Safari 对
toggle事件的支持较晚(iOS 15.4+),旧版本需降级为click+ 状态轮询
什么时候该放弃 <details></details>,改用手动 JS 实现
当项目有以下任一需求时,原生方案就不再适用:
- 需要兼容 IE 或旧版 Android WebView(
<details></details>在 IE 中完全不支持) - 要求内容淡入淡出、缩放等非高度类动画
- 折叠区域要参与 CSS Grid/Flex 的自动尺寸计算(
<details></details>在open状态下仍可能影响父容器布局) - 服务端渲染需预设展开状态,且首屏不能闪动(
<details open></details>在 SSR 中可能被浏览器忽略)
此时应退回到 <button></button> + <div> + JS 控制 <code>max-height 或 aria-expanded 的组合,重点是补全 aria-controls 和焦点管理,否则可访问性会断层。











