优先使用 details + summary 实现手风琴,因其为原生 html5 元素,自带展开/收起状态、键盘操作(空格/回车)及屏幕阅读器支持,可访问性良好;css 仅负责样式,不干涉逻辑;现代浏览器均支持,ie 不支持;动画可用 fade+slide 更稳妥,高度过渡易卡顿;js 仅在需旧浏览器支持或复杂交互时引入,须手动维护 aria-expanded/aria-hidden 和焦点管理。

直接用 details + summary 就能实现语义化、无障碍、无需 JS 的手风琴,但浏览器兼容性和样式控制有限;需要更灵活的交互或旧浏览器支持时,才必须上 JS + CSS。
为什么优先用 details 和 summary?
这是原生 HTML5 元素,自带展开/收起状态、键盘操作(空格/回车触发)、屏幕阅读器支持,且默认可访问性良好。CSS 只需处理外观,不干涉逻辑。
-
details元素默认折叠,open属性表示当前展开 -
summary是唯一允许作为details第一个子元素的标签,点击即切换状态 - 所有现代浏览器都支持(Chrome 12+、Firefox 49+、Safari 6.2+),IE 完全不支持
- 不能用 CSS 选中 “未展开时的
details内容”,因为它是 display: none 的,动画需靠details[open]配合max-height或height过渡(但有高度限制)
用 CSS 实现平滑展开动画(仅限 details)
原生 details 不支持 transition 直接作用于 height(因为内容高度不确定),常用折中方案是用 max-height 模拟:
details {
margin-bottom: 8px;
}
details summary {
list-style: none;
padding: 12px 16px;
background: #f5f5f5;
cursor: pointer;
}
details summary::marker {
content: "+";
}
details[open] summary::marker {
content: "−";
}
details[open] > *:not(summary) {
animation: slideDown 0.3s ease-out;
}
@keyframes slideDown {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
注意:这里没用 max-height 过渡,因为容易因内容高度不准导致卡顿;改用淡入+位移动画更稳妥。若坚持高度过渡,max-height 值必须设为略大于内容最大可能高度(如 max-height: 500px),否则会“抽搐”。
需要 JS 控制时,关键在 aria-expanded 和 aria-hidden
手动实现时,可访问性不是锦上添花——是必须补上的。JS 只负责切换状态和属性,样式仍由 CSS 驱动:
- 给触发按钮加
aria-expanded="true/false",对应内容区加aria-hidden="true/false" - 用
tabindex="0"确保键盘可聚焦,监听keydown处理空格/回车 - CSS 中用
[aria-expanded="true"] + .accordion-content显示内容,避免依赖 class 切换 - 不要只靠
display: none隐藏内容——它会让屏幕阅读器完全忽略;配合aria-hidden才安全
多个 Accordion 同时展开 or 互斥?看需求定 DOM 结构
原生 details 默认各自独立,互不影响;JS 实现时,是否互斥取决于你是否在展开前主动收起其他项:
- 若要“单开”(一次只一个展开),JS 中需遍历所有兄弟
details或同类容器,移除其open属性或aria-expanded="true" - 若要“多开”,什么也不用做——每个项状态自治
- DOM 上建议用
class="accordion-item"包裹每组标题+内容,方便 JS 选择和样式隔离 - 别用
id做 JS 绑定依据——动态生成或 SSR 场景下易冲突,优先用相对定位(如closest(".accordion-item"))
最常被忽略的是焦点管理:展开后,如果内容里有可聚焦元素(比如链接、按钮),应把键盘焦点移到第一个可聚焦子元素上,否则视障用户会卡在空白处。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











