details 和 summary 是 html5 原生语义化折叠组件,需严格遵循结构(summary 必为 details 的首个直接子元素),定制样式应清除默认 marker 并用 ::before 插入图标,动画用 max-height 实现,状态监听用 toggle 事件而非 click。

details 和 summary 是 HTML5 原生支持的语义化折叠组件,不用写一行 JavaScript 就能实现可访问、可键盘操作、对屏幕阅读器友好的交互。关键不在于“能不能用”,而在于“怎么用才不出错、不踩坑”。
结构必须严格:summary 必须是 details 的第一个直接子元素
这是最常出错的地方。浏览器只认这种结构:
-
<details><summary>标题</summary>内容</details>✅ -
<details><p>说明</p> <summary>标题</summary></details>❌(summary 不是首个子元素) -
<details><div><summary>标题</summary></div>内容</details>❌(summary 不是直接子元素)
空格、换行、注释或包裹容器都会破坏语义,尤其在 Safari 中直接失效。服务端模板或 JSX 输出时建议压缩 HTML 或写成单行。
样式定制要绕开原生箭头限制
浏览器对 summary::marker 的支持不统一,Safari 旧版本几乎不响应 rotate 或 content 替换。更稳妥的做法是:
- 先清除默认样式:
summary { list-style: none; } - 用
summary::before插入图标:content: "▶"; margin-right: 6px; - 展开时切换:
details[open] summary::before { content: "▼"; transform: rotate(0deg); } - 加过渡:
transition: transform 0.2s, content 0.2s;
别给 summary 设 display: block 或 flex,否则可能影响点击热区或图标对齐。
加展开动画得用 max-height 技巧
details 本身不支持 height 过渡,因为高度由内容决定,height: auto 无法参与 transition。正确做法是:
- 给内容包一层
<div class="details-content"> <li>初始状态:<code>.details-content { max-height: 0; overflow: hidden; transition: max-height 0.3s ease-in-out; } - 展开状态:
details[open] .details-content { max-height: 400px; }(数值需略大于内容实际高度) - 它在 open 属性真实变更后触发,兼容键盘(空格/回车)和脚本设置
details.addEventListener('toggle', () => { console.log('当前状态:', details.open); });- 检测是否展开,优先用
el.hasAttribute('open'),而不是读el.open(后者始终返回布尔值,但属性存在与否才是渲染依据) - JS 控制状态推荐
el.open = true或el.toggleAttribute('open'),避免用setAttribute('open', '')导致状态错乱
设太小会裁剪内容,太大则收起时留明显空白。若内容高度差异大,可 JS 动态计算后设 inline style,但纯 CSS 场景下预估一个安全值更实用。
状态监听与 JS 增强要选对事件
想响应展开/收起动作,别监听 click,应监听 toggle 事件:
多个 details 实现手风琴效果(单开模式)必须用 JS 控制,原生不支持 name 属性互斥。











