原生 details + summary 是最轻量、无障碍友好的展开/折叠方案,无需 js 即可实现;若需自定义或兼容旧浏览器,则须用 js 控制 display 或 max-height,并同步 aria-expanded、管理焦点与键盘操作。

点击按钮触发 details 元素的展开/折叠
原生 details + summary 是最轻量、无障碍友好的方案,不用 JS 就能实现点击切换。但注意:它默认只支持单个 summary 作为触发器,且不能直接用普通 <button></button> 替换 summary(否则会破坏语义和键盘操作)。
- 写法必须是
<details><summary>标题</summary>内容</details>,浏览器自动绑定点击逻辑 - 想自定义按钮样式?用 CSS 重置
summary的默认外观,比如summary { list-style: none; outline: none; } - JS 控制开关状态?直接操作
details.open属性:document.querySelector('details').open = true - 不支持 IE,Edge 79+、Chrome 12+、Firefox 49+ 才可用
用普通 <button></button> 控制任意元素显隐(兼容老浏览器)
如果必须用 <button></button>、或要兼容 IE、或内容不在 details 内部,就得手写 JS 切换 display 或 max-height。关键不是“显示/隐藏”,而是“是否保留文档流”和“是否有过渡动画”。
- 纯显隐用
element.style.display = 'block' / 'none'最快,但无过渡,且会丢失 height 动画 - 要做平滑收起?改
max-height+overflow: hidden,但需预设一个足够大的值(如max-height: 500px),否则动画卡顿 - 更稳妥的做法是用
height: auto+scrollHeight动态计算:先设height: 0,再读取element.scrollHeight,最后设为该值 - 记得在切换后移除内联
style,避免干扰 CSS 优先级
避免多次绑定导致重复执行
常见错误是把事件监听写在函数里反复调用,每次点击都新增一个监听器,结果点一次触发多次。
- 监听器必须只绑定一次——放在初始化逻辑里,不要塞进点击回调中
- 如果按钮是动态生成的,用事件委托:
document.addEventListener('click', e => { if (e.target.matches('[data-toggle]')) { ... } }) - 用
data-*属性标记目标元素,比如<button data-target="#faq1"></button>,比靠 class 或位置找更可靠 - 检查是否已存在监听器?不用复杂判断,直接用
addEventListener的第三个参数{ once: true }(如果只需触发一次)
aria-expanded 和键盘可访问性不是可选项
光让鼠标点开没用。屏幕阅读器用户依赖 aria-expanded 告知当前状态,Enter/Space 键也必须响应。
- 每次切换显隐,同步更新
button.setAttribute('aria-expanded', 'true')或'false' - 给按钮加
tabindex="0"(如果本身不可聚焦),并监听keydown处理 Enter 和 Space - 展开后,焦点应移到内容区首个可聚焦元素(如第一个
<a></a>或<input>),否则键盘用户会迷失 - 别用
visibility: hidden隐藏内容——它仍被屏幕阅读器读取;用display: none或aria-hidden="true"配合控制
实际做时,details 能覆盖大多数场景;真要定制交互,重点不在“怎么让它动”,而在“怎么让它对所有人真正可用”。很多 bug 出现在状态不同步、焦点没管理、或者 aria 属性漏更新上。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











