原生 HTML 的 和 标签可零 JavaScript 实现折叠展开,语义化、无障碍友好、性能更优;需注意 summary 必为首个子元素、样式重置、open 属性控制及框架 SSR 一致性。

用 details 和 summary 最快实现折叠展开
不用写一行 JavaScript,原生 HTML 就能搞定。浏览器支持已很成熟(Chrome 12+、Firefox 49+、Safari 6+、Edge 79+),移动端也完全可用。
关键不是加 class 或监听 click,而是语义化标签本身自带行为:
<details>
<summary>点击查看活动规则</summary>
<p>1. 活动时间:2024年10月1日–10月7日</p>
<p>2. 参与方式:下单满99元自动获得抽奖资格</p><p><span>立即学习</span>“<a href="https://pan.quark.cn/s/cb6835dc7db1" style="text-decoration: underline !important; color: blue; font-weight: bolder;" rel="nofollow" target="_blank">前端免费学习笔记(深入)</a>”;</p><div class="aritcle_card flexRow">
<div class="artcardd flexRow">
<a class="aritcle_card_img" href="/xiazai/skill11244" title="Convert Markdown To HTML And PDF - Markdown和HTML、PDF互转"><img
src="https://img.php.cn/upload/skill/001/246/273/6adaf20d79f23518.jpg" alt="Convert Markdown To HTML And PDF - Markdown和HTML、PDF互转" ></a>
<div class="aritcle_card_info flexColumn">
<a href="/xiazai/skill11244" title="Convert Markdown To HTML And PDF - Markdown和HTML、PDF互转">Convert Markdown To HTML And PDF - Markdown和HTML、PDF互转</a>
<p>实现 Markdown↔ HTML 互转,支持导出 PDF,用于用户请求的文档格式转换。</p>
</div>
<a href="/xiazai/skill11244" title="Convert Markdown To HTML And PDF - Markdown和HTML、PDF互转" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>
</div>
</div>
</details>
-
details默认收起,点击summary文字即可展开/收起 -
summary必须是details的第一个子元素,否则不生效 - 首次加载时默认闭合;如需默认展开,加
open属性:<details open></details> - 不能嵌套多个
summary,只允许一个,多余会被忽略
自定义箭头和样式必须重置 summary 的默认行为
浏览器会给 summary 加默认箭头(如 ▶ / ▼)并设为 display: list-item,直接写 CSS 容易失效。
- 先清除原生样式:
summary { list-style: none; display: block; } - 箭头建议用伪元素控制,避免干扰可访问性:
summary::after { content: "▼"; margin-left: 4px; } - 展开态用
details[open] summary::after { content: "▲"; }切换 - 不要给
summary设cursor: pointer—— 它本来就是可点击的,加了反而可能掩盖屏幕阅读器识别
需要 JS 控制展开/收起时,操作 open 属性而非 class
如果页面有“全部展开”“一键收起”按钮,或要和表单联动,直接改 DOM 属性最可靠。
- 展开:
document.querySelector('details').open = true - 收起:
document.querySelector('details').open = false - 监听变化用
toggle事件:el.addEventListener('toggle', () => {...}),不是click - 别用
el.setAttribute('open', '')或removeAttribute('open')—— 这样会丢失布尔属性语义,且 IE 不兼容 - 批量操作时注意:
details元素不支持querySelectorAll('details').forEach(...)直接赋值open,得用传统 for 循环或解构数组
和 Vue/React 等框架一起用要注意 SSR 和 hydration
服务端渲染时若初始状态是 open,但客户端 hydrate 后没同步,会出现闪动或状态错乱。
- Vue 中避免在模板里直接写
<details :open="showRules"></details>——open是布尔属性,应写作<details :open.sync="showRules"></details>或用v-bind:open="showRules ? '' : null" - React 中用
open={showRules}即可,但首次渲染前确保 state 初始化一致 - 所有框架下,都不要在
useEffect或mounted里再手动设置open,除非你明确要覆盖初始态 - 无障碍仍有效:屏幕阅读器能正确读出
details[open]状态,无需额外aria-expanded
真正容易被忽略的是:很多人试图用 max-height + transition 模拟折叠,结果滚动错位、内容截断、动画卡顿——其实 details 天然支持流式高度计算,也不触发重排,更轻量也更健壮。


















