原生是最快、最语义化且无障碍支持最佳的折叠组件,无需JS即可响应键盘操作,但默认不互斥、无动画、样式受限。

原生 <details></details> + <summary></summary> 就是最快、最语义化、无障碍支持最好的折叠展开框,不需要 JS 也能响应键盘操作;但默认不互斥、无动画、样式受限,别指望它“开箱即用”就满足所有设计需求。
用 <details></details> 和 <summary></summary> 搭建基础容器
这是唯一无需 JS 就能天然支持空格/回车触发、屏幕阅读器播报、焦点管理的方案。关键点很实在:
-
<summary></summary>必须是<details></details>的第一个子元素,写成<summary></summary>或塞在<p></p>里都会失效 - 默认收起,加
open属性可默认展开:<details open><summary>标题</summary>内容</details></li> <li>Safari 15.4+ 才修复 <code><summary>
的 focus 样式 bug,旧版中点击后可能失焦,别依赖 outline 样式做状态反馈 - 不要给
<details></details>或<summary></summary>设height或display: none—— 这会破坏可访问性,且部分浏览器直接忽略
让多个 <details></details> 实现手风琴(单开)效果
原生允许多开,要单开必须加 JS 控制 open 属性。监听 toggle 事件是唯一可靠方式,click 会漏掉键盘操作。
- 给所有面板加统一 class,比如
class="accordion-item" - 监听父容器上的
toggle事件(推荐事件委托),或遍历每个<details></details>单独绑定 - 只在当前项
event.target.open === true时,对其他同组项调用.removeAttribute('open')—— 别用.open = false,否则会再次触发toggle,造成循环 - 动态插入的新
<details></details>不会自动继承逻辑,要么重新绑定,要么改用委托
加平滑展开动画:绕过 <details></details> 的限制
原生不支持 height 或 max-height 过渡,因为内容高度是动态计算的。想有动画就得自己接管高度控制。
立即学习“前端免费学习笔记(深入)”;
- 放弃
<details></details>的原生交互,改用<div> + <code>max-height+overflow: hidden+ JS 测高 - 展开前读取目标内容的
scrollHeight,设为max-height;收起时设为0 -
overflow: hidden必须始终存在,否则动画中内容会溢出 - 别硬写死
max-height: 500px,长内容会被截断,短内容动画拖沓 - 如果内容含图片或字体加载延迟,
scrollHeight可能不准,需监听load或fontloaded后重设 - 隐藏默认箭头最稳写法:
summary { list-style: none; },比summary::marker { content: ""; }兼容性好 - 加旋转图标用
summary::before,配合details[open] summary::before { transform: rotate(90deg); } -
transform动画必须配transition,且transform-origin建议设为left center避免偏移 - 别在
<summary></summary>里放<button></button>或链接——点击会冒泡触发折叠;要么加event.stopPropagation(),要么把按钮挪到<summary></summary>外、<details></details>内
自定义图标和样式时容易踩的坑
原生箭头不可靠,Safari 对 ::marker 支持差,得用 ::before 补图标,但旋转动画要小心。
真正难的不是搭出能动的框,而是确保键盘用户能 tab 进去、屏幕阅读器能念出状态、小屏下不会挤出横向滚动条、图片还没加载完时高度也不崩。这些细节一旦漏掉,再漂亮的动画也白搭。



















