CSS项目实战之FAQ手风琴效果_纯CSS实现内容展开收起

1次阅读

details+summary是最简方案但少用,因默认样式差且ie全系不支持;虽语义化、无障碍友好、性能优,但动画实现难、互斥需js、移动端点击区优化复杂。

CSS项目实战之FAQ手风琴效果_纯CSS实现内容展开收起

为什么 details + summary 是最简方案,但很多人不用?

因为默认样式太难看,且老浏览器不支持。chrome 12+、firefox 49+、safari 6.2+ 支持,edge 79+ 也行;IE 全系不支持。如果你的项目要兼容 IE 或旧安卓 webview,就得换方案。

  • details 是语义化原生标签,无需 JS,无障碍友好(自动处理焦点、键盘展开/收起)
  • summary 点击即切换 open 属性,css 可用 details[open] > div 控制内容显隐
  • 常见错误:直接给 summarycursor: pointer 却忘了它本身已是可点击元素,反而干扰屏幕阅读器识别
  • 性能上比 JS 方案更轻——无事件监听、无 dom 操作、无重排触发
<details>   <summary>常见问题</summary>   <div>这里是答案内容</div> </details>

用 CSS 实现过渡动画时,max-height 为什么总失效?

因为 max-height 动画依赖一个“已知的固定最大值”,而内容高度是动态的。设成 max-height: 0max-height: 500px 看似可行,但一旦内容超过 500px 就会截断;设成 max-height: 100vh 又可能在小屏上抖动。

  • 正确做法:用 height: auto 配合 overflow: hidden,再靠 JS 测量真实高度后写入内联 style,CSS 只负责过渡 height
  • 更轻量的替代:改用 opacity + transform: scaleY(),视觉上更顺滑,且不依赖高度计算
  • 容易踩的坑:对 summarydisplay: none 切换,会导致 detailsopen 状态丢失,且无法键盘操作

纯 CSS 方案里,如何让多个 FAQ 互斥展开(手风琴逻辑)?

原生 details 不提供互斥能力,必须靠 JS 控制。但如果你坚持“纯 CSS”,只能用单选框(input[type="radio"])模拟:

  • 每个 FAQ 对应一个 input#faq1 + label[for="faq1"] + 内容区
  • input:checked ~ .answer 控制显示,input:not(:checked) ~ .answer 控制隐藏
  • 所有 radio 共享同一个 name,天然互斥
  • 缺点:失去语义化,键盘 Tab 顺序混乱,屏幕阅读器需额外 aria-controls 补充
  • 参数差异:不能用 open 属性控制状态,也无法用 JS 直接读取“是否展开”,得查 checked

移动端点击区域太小,summary 怎么安全扩大又不破坏可访问性?

不要给 summarypaddingmin-height 后直接覆盖整个行高——这会让 VoiceOver 误判点击区域,且在 ios Safari 中可能触发双击缩放。

立即学习前端免费学习笔记(深入)”;

  • 推荐做法:用 summary::before 伪元素撑开点击热区,同时保持文本内容区域不变
  • 示例:summary::before { content: ""; position: absolute; top: -10px; bottom: -10px; left: -16px; right: -16px; }
  • 必须加 position: relativesummary 父容器,否则伪元素定位错乱
  • 兼容性注意:iOS 15.4 之前,summary::before 在某些 webkit 版本中不触发点击,稳妥起见可加一层透明 span 包裹文本并设 pointer-events: none

复杂点在于:视觉动效、语义正确、键盘可操作、屏幕阅读器播报、移动端点击容错——这五件事很难全靠纯 CSS 对齐。多数人卡在“以为动画=体验好”,其实用户更需要的是状态明确、反馈及时、不意外跳转。

text=ZqhQzanResources