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

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



















