原生 和 是实现 faq 折叠最轻量、语义化、可访问的方案,现代浏览器均支持,无需 js 或 css 框架; 必须为 首子元素,支持键盘操作与屏幕阅读器,禁用 tabindex="-1" 和嵌套,需手动添加 padding 保证可读性。

用 <details></details> 和 <summary></summary> 实现原生折叠 FAQ
不需要 JS 或 CSS 框架,现代浏览器(Chrome 12+、Firefox 49+、Safari 12.1+、Edge 79+)都支持 <details></details>。它天然语义化、可访问、自带展开/收起状态,是实现 FAQ 折叠最轻量也最可靠的方式。
常见错误是手动写 onclick + display: none,既增加维护成本,又破坏键盘导航(比如无法用空格/回车触发)、屏幕阅读器识别困难。
-
<summary></summary>必须是<details></details>的第一个子元素,否则无法触发折叠行为 - 默认是收起状态;加
open属性可默认展开:<details open></details> - 不支持嵌套
<details></details>(部分浏览器会失效),如需多级,得用 JS 控制 - 样式可覆盖,但不要用
display: contents或visibility: hidden隐藏<summary></summary>,否则丧失可访问性
让折叠项支持键盘操作和屏幕阅读器
原生 <details></details> 默认支持空格/回车切换状态,<summary></summary> 自动获得 role="button" 和 aria-expanded 属性(浏览器自动注入)。但要注意:
- 别给
<summary></summary>加tabindex="-1"—— 会切断键盘焦点流 - 如果自定义图标(比如 ▶ / ▼),要用
aria-hidden="true"标记,避免重复读出 - 不要在
<summary></summary>里放链接或按钮,否则交互冲突;如有需要,改用 JS 方案 - 展开内容区域默认有
aria-hidden="true"(收起时)或aria-hidden="false"(展开时),无需手动设置
兼容老浏览器(IE / 旧 Safari)的降级处理
IE 完全不支持 <details></details>,旧 Safari(
检测方式很简单:
if ('open' in document.createElement('details')) { /* 支持 */ } else { /* 降级逻辑 */ }
- 降级时,用
class="faq-item"包裹每条问答,JS 控制max-height+overflow实现动画收起 - 避免依赖
height: 0动画——内容高度不确定时容易错位,推荐用max-height过渡 - 务必同步维护
aria-expanded和aria-controls,否则对辅助技术不友好 - 如果项目已用 Vue/React,直接用组件封装更稳妥,但注意 SSR 时服务端要输出完整 DOM,不能依赖客户端 JS 渲染
样式微调时容易忽略的细节
<details></details> 的默认样式极简,但有些 CSS 重置会意外破坏它。比如:
- 全局
summary { list-style: none; }会删掉默认箭头(Chrome/Firefox 显示小三角),应改为summary::marker { content: "▶"; }自定义 -
details[open] > summary::after不可靠,不同浏览器 marker 渲染机制不同,推荐用伪元素统一控制图标 - 展开后内容区域没有默认 padding/margin,需手动加,否则文字贴边难读
- 移动端点击区域太小?给
<summary></summary>加padding即可,它天然响应点击,无需额外cursor: pointer
折叠 FAQ 看似简单,真正麻烦的是可访问性保障和边界场景——比如用户禁用 JS、缩放 200%、只用键盘导航。原生 <details></details> 能扛住大部分,但一旦加了图标动画、多级嵌套或服务端渲染,就得一层层验逻辑。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











