原生 和 可零 js 实现语义化、可访问的折叠面板, 必为 首子元素,默认收起,加 open 属性可默认展开,支持嵌套;需用 ::marker 控制箭头,监听 toggle 事件获取状态,ie 及旧 webview 需 js 回退。

用 <details></details> 和 <summary></summary> 最快实现原生折叠面板
不用 JS、不引 CSS 框架,HTML5 原生就支持折叠面板。核心是 <details></details> 元素,它自带展开/收起状态管理,<summary></summary> 作为触发标题 —— 浏览器自动处理 toggle 逻辑和 ARIA 属性,语义正确、可访问性好。
常见错误是把 <summary></summary> 写成多个或放在 <details></details> 外面,这会导致点击无效或控制错位。它必须是 <details></details> 的第一个子元素。
-
<details></details>默认收起;加open属性可默认展开:<details open></details> -
<summary></summary>内容会显示为可点击标题,右侧浏览器自动加箭头(样式可覆盖) - 内部可放任意 HTML:段落、列表、表单甚至另一个
<details></details>(支持嵌套) - 注意 Safari 旧版本对
<details></details>的transition支持弱,做高度动画需额外 JS
想自定义箭头或去掉默认样式?用 CSS 覆盖 <summary></summary>
浏览器给 <summary></summary> 加了 list-style 箭头,且部分浏览器(如 Chrome)默认有 margin。要统一外观,必须重置:
summary {
list-style: none;
margin: 0;
}
summary::marker {
content: "▶ ";
}
details[open] > summary::marker {
content: "▼ ";
}
关键点:
- 不能只靠
list-style: none清掉箭头,Firefox 需要::marker才能控制 -
details[open] > summary::marker是精准匹配展开态,避免影响其他<summary></summary> - 别用
display: block或flex改<summary></summary>显示方式,可能破坏点击区域
需要 JS 控制展开状态?监听 toggle 事件,别用 click
<details></details> 的状态切换由浏览器内部触发,不是简单 click。直接绑 click 事件会漏掉键盘操作(如空格键)、屏幕阅读器交互,且在 Safari 中可能失效。
正确做法是监听 toggle 事件:
const details = document.querySelector('details');
details.addEventListener('toggle', () => {
console.log('当前状态:', details.open);
});
-
toggle事件在用户交互(鼠标、键盘、辅助技术)后触发,时机可靠 -
details.open是实时布尔值,比查 DOM class 更直接 - 不要在
toggle里调details.open = true/false,会引发循环触发
兼容 IE 或老安卓 WebView?得回退到 JS 实现
<details></details> 在 IE 完全不支持,Android 4.4 及更早 WebView 也无效。如果项目必须覆盖这些环境,得手写 JS 折叠逻辑:
- 用
<div class="accordion-item"> + <code><button></button>+<div class="panel"> 结构 <li>通过 <code>aria-expanded和aria-controls手动同步可访问性属性 - 用
max-height过渡或height: 0 / auto+scrollHeight计算做动画(注意height: auto无法过渡) - 务必测试键盘 Tab + Enter/Space 触发,否则残障用户无法使用
原生 <details></details> 看似简单,但它的默认行为、语义、可访问性是手工实现很难完全对齐的。除非明确要支持 IE,否则优先用原生方案 —— 复杂点在于样式微调和事件监听习惯,而不是功能本身。











