原生 details+summary 是实现 FAQ 展开收起的最简、语义正确且无障碍友好的方案,无需 JS 即支持键盘操作与屏幕阅读器,仅在需单选折叠或持久化状态时才用 JS 增强。

用 details 和 summary 最简实现 FAQ 展开收起
现代浏览器原生支持 details + summary,不用 JS 就能做可点击展开的 FAQ,语义正确、无障碍友好、代码极简。
常见错误是手动写 div + onclick + CSS 切换类名,结果键盘无法操作、屏幕阅读器不识别、还容易漏掉 aria-expanded 等属性。
-
details默认收起,open属性可设初始展开状态(如<details open>) -
summary必须是details的第一个子元素,否则不生效 - 点击
summary文本或其右侧默认三角图标均可触发展开/收起 - 所有主流浏览器(Chrome/Firefox/Safari/Edge)均支持,IE 不支持 —— 若需兼容 IE,才需 JS 回退
去掉默认三角图标并自定义箭头方向
浏览器给 summary 渲染的下拉三角样式不统一,且收起/展开时方向不变,体验差。用 CSS 重置并动态控制箭头更可控。
- 先用
summary::marker { content: "" }清除原生图标(注意 Safari 需额外加-webkit-appearance: none) - 用伪元素
summary::after添加自定义箭头,例如content: "▶" - 利用
details[open] summary::after { content: "▼" }实现方向切换 - 避免用背景图或 SVG 图标——增加请求、缩放失真,纯字符或
transform: rotate(90deg)更轻量
让 FAQ 列表支持键盘操作与屏幕阅读器
原生 details 已内置键盘支持(空格/回车触发),但部分定制样式会破坏焦点可见性或 ARIA 状态。
立即学习“前端免费学习笔记(深入)”;
- 确保
summary可获得焦点:不要加tabindex="-1",也不要设outline: none后不提供替代焦点样式 - 不需要手动加
aria-expanded或aria-controls—— 浏览器自动管理,加了反而可能冲突 - 若内容含表单、链接等交互元素,收起时它们应不可聚焦(
details:not([open]) *:focus { outline: none }不推荐;正确做法是依赖原生行为,它已处理) - 测试方式:Tab 键导航到
summary,按空格键看是否展开 —— 这比鼠标点击更能暴露可访问性问题
需要 JS 控制时只做增强,别重写逻辑
只有两种合理场景需 JS:一是强制关闭其他项(单选折叠)、二是记录用户上次展开项(持久化)。其余情况都该交给原生 details。
- 监听
toggle事件(不是click),它在展开/收起后触发,且包含event.target.open状态 - 单选逻辑示例:
document.querySelectorAll("details").forEach(d => d.addEventListener("toggle", () => { if (d.open) [...d.parentNode.children].filter(el => el !== d).forEach(el => el.open = false); })) - 避免用
querySelectorAll("summary")绑 click —— 会干扰原生行为,且toggle事件更准确(比如通过键盘触发时也响应) - localStorage 存储展开状态时,key 应基于
summary文本或 data-id,而非索引 —— DOM 结构变动后索引会错位
最易被忽略的是:以为加了 JS 就更“强大”,结果破坏了语义、可访问性和默认动效。原生 details 的展开动画是硬编码的(无 CSS 过渡),强行用 JS + height 动画反而更卡、更难维护。



















