details + summary 是最轻量、语义正确、无需 js 的折叠方案,适合 faq 等静态场景;但不支持高度动画、自动滚动、多组互斥等,需按需取舍。

details + summary 是最轻量、语义正确、无需 JS 就能工作的折叠方案,适合 FAQ、帮助文档、表单分组等静态或低交互场景。但它不支持平滑高度动画、不能默认收起后再展开时自动滚动到顶部、多组互斥需手动加 JS——别硬套,先看是否真需要这些。
为什么直接用 details 比写 JS 切换 display 更稳妥
原生 details 自带键盘可访问性(空格/回车触发)、屏幕阅读器识别、焦点管理、ARIA 语义(aria-expanded 自动同步),而手写 display: none/block 很容易漏掉 tabindex、aria-hidden、焦点恢复等细节。尤其在表单页里,用户用键盘导航时,手写方案常导致焦点丢失或跳过内容。
-
summary必须是details的第一个子元素,否则折叠逻辑失效 - 不要用
click监听summary,改用details上的toggle事件,否则键盘操作不触发回调 - IE 完全不支持
details,如需兼容,得用if ('open' in document.createElement('details'))做特性检测,再加载降级 JS 方案
怎么让 details 看起来不像“浏览器默认样式”
浏览器对 summary 前的箭头使用 ::marker 渲染,但 Safari 对 ::marker 支持不稳定,所以统一用 ::before 替代:
summary {
list-style: none;
position: relative;
padding-left: 24px;
}
summary::before {
content: "▶";
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
transition: transform 0.2s;
}
details[open] summary::before {
content: "▼";
transform: translateY(-50%) rotate(90deg);
}
- 别依赖
details[open]在旧 Safari 中精准匹配,可配合 JS 添加is-openclass 做兜底 - 如果内容含图片或视频,
details展开后不会自动 lazy-load,需手动监听toggle事件并调用loading="lazy"或触发IntersectionObserver
多个 details 实现手风琴互斥,JS 只需 4 行
原生 details 默认各自独立,要实现“点一个、其他关掉”,必须监听 toggle 并遍历兄弟节点:
document.querySelectorAll('details').forEach(d => {
d.addEventListener('toggle', () => {
if (d.open) {
document.querySelectorAll('details').forEach(other => {
if (other !== d) other.open = false;
});
}
});
});
- 只在
d.open === true时执行关闭,避免收起动作误触发二次关闭 - 动态插入新
details(如通过innerHTML或框架渲染)后,这段代码不会自动绑定,得重新运行或改用事件委托(但toggle不冒泡,委托不可行,只能重绑) - 如果页面有上百个
details,频繁设open = false会触发多次重排,此时建议改用 CSS 类控制 +max-height动画方案
需要 height 过渡动画?放弃 details,改用 max-height + JS 测高
details 不支持 height 或 max-height 过渡,因为展开时高度是自动计算的。真要动画,就得自己接管:
- 用
element.scrollHeight获取内容真实高度,设为max-height,再加transition: max-height 0.3s ease - 收起后必须设
max-height: 0+overflow: hidden,否则可能残留滚动条或撑开布局 - 内容高度变化(比如文字大小随 viewport 缩放)时,
scrollHeight不会自动更新,得监听resize或用ResizeObserver重算 - 动画结束时若需执行回调(如懒加载图片),不能靠
transitionend,因为max-height从0→scrollHeight的过渡可能因精度问题提前结束,建议用setTimeout延迟触发
真正难的不是写出能动的折叠面板,而是判断什么时候不该用动画:移动端小屏下,一次展开多个面板可能撑满视口,用户反而要滚半天才能看到按钮;内容含 iframe 或第三方 widget 时,强制 max-height 可能截断其 UI;而纯文本 FAQ,details 的瞬时切换其实更符合用户预期——快,且无干扰。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











