折叠面板不用 JS?<details> 和 <summary> 标签的隐藏用法
关键词: HTML5、details、summary、折叠面板、无 JS、交互组件
一、前言:一个被严重低估的 HTML 标签
提到"折叠面板"或"手风琴效果",大多数前端开发者的第一反应是:
"写一堆
div,绑onClick,用useState控制isOpen......"
但你可能不知道,HTML5 早就内置了一个原生折叠组件,不需要写一行 JavaScript:
css
<details>
<summary>点击展开</summary>
<p>这里是折叠的内容</p>
</details>
就这三行,一个可展开/收起的面板就完成了。
浏览器原生支持,自带动画(部分浏览器),语义清晰,可访问性满分。
今天我们就来深挖这对"冷门兄弟"------<details> 和 <summary> 的所有隐藏玩法。
二、基本用法
2.1 最简单的折叠面板
css
<details>
<summary>什么是 HTML5 details 标签?</summary>
<p>
details 是 HTML5 引入的一个原生交互元素,
用于创建可展开/收起的披露组件。
</p>
</details>
渲染效果:
- 默认收起,只显示
<summary>的内容 - 点击
<summary>区域,内容展开 - 再次点击,内容收起
2.2 默认展开
加一个 open 属性即可:
css
<details open>
<summary>默认展开的内容</summary>
<p>页面加载时我就是打开的。</p>
</details>
2.3 语义结构
xml
<details>
<summary>摘要/标题(必填,但可省略)</summary>
<!-- summary 之后的所有内容都是可折叠区域 -->
<p>正文内容......</p>
<ul>
<li>列表项</li>
<li>列表项</li>
</ul>
</details>
⚠️ 注意: 如果省略
<summary>,浏览器会自动生成一个默认的 "Details" 文本作为标题。
三、样式定制:告别默认三角
3.1 隐藏默认标记
不同浏览器对 <summary> 的默认三角标记渲染不同。统一隐藏:
css
summary {
list-style: none; /* 标准 */
}
summary::-webkit-details-marker {
display: none; /* Safari/Chrome */
}
3.2 自定义展开/收起图标
xml
<details class="custom-details">
<summary>
<span class="title">点击展开</span>
<span class="icon">▶</span>
</summary>
<p>折叠内容......</p>
</details>
<style>
.custom-details summary {
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
padding: 12px;
background: #f5f5f5;
border-radius: 6px;
}
.custom-details[open] .icon {
transform: rotate(90deg);
}
.icon {
transition: transform 0.2s ease;
}
.custom-details p {
padding: 12px;
margin: 0;
}
</style>
3.3 用 CSS 实现平滑展开动画
<details> 的 open 属性切换是瞬间的,没有过渡动画。但我们可以用 CSS Grid 模拟:
css
details .content {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 0.3s ease;
}
details[open] .content {
grid-template-rows: 1fr;
}
details .content > div {
overflow: hidden;
}
css
<details>
<summary>带平滑动画的面板</summary>
<div class="content">
<div>
<p>这段内容会平滑地展开和收起。</p>
<p>利用 CSS Grid 的 fr 动画实现。</p>
</div>
</div>
</details>
🎉 纯 CSS 动画,零 JavaScript!
四、实战场景
4.1 FAQ 常见问题
xml
<section class="faq">
<h2>常见问题</h2>
<details>
<summary>这个产品支持退款吗?</summary>
<p>支持 7 天无理由退款,详情请查看退款政策。</p>
</details>
<details>
<summary>如何联系客服?</summary>
<p>请发送邮件至 support@example.com,我们会尽快回复。</p>
</details>
<details>
<summary>支持哪些支付方式?</summary>
<p>我们支持信用卡、支付宝、微信支付和 PayPal。</p>
</details>
</section>
<style>
.faq details {
border-bottom: 1px solid #ddd;
padding: 12px 0;
}
.faq summary {
font-weight: 500;
cursor: pointer;
padding: 8px 0;
}
.faq p {
margin: 8px 0 0;
color: #666;
}
</style>
4.2 手风琴菜单(互斥展开)
这个场景确实需要一点点 JavaScript,但极其轻量:
xml
<details name="faq" open>
<summary>问题一</summary>
<p>答案一</p>
</details>
<details name="faq">
<summary>问题二</summary>
<p>答案二</p>
</details>
<details name="faq">
<summary>问题三</summary>
<p>答案三</p>
</details>
dart
// 利用 name 属性实现互斥(实验性特性,部分浏览器支持)
// 或者手动实现:
document.querySelectorAll('details').forEach(detail => {
detail.addEventListener('toggle', () => {
if (detail.open) {
document.querySelectorAll('details').forEach(d => {
if (d !== detail) d.open = false;
});
}
});
});
💡 好消息: 最新的 HTML 规范正在引入
name属性来实现原生互斥(类似<input type="radio">),未来可能完全不需要 JS!
4.3 图片预览/隐藏剧透
xml
<details class="spoiler">
<summary>⚠️ 点击查看剧透</summary>
<p>其实凶手就是那个看起来最无辜的管家。</p>
</details>
<style>
.spoiler summary {
color: #e74c3c;
font-weight: bold;
}
</style>
4.4 代码块折叠
less
<details class="code-block">
<summary>📄 main.js(点击展开代码)</summary>
<pre><code>function hello() {
console.log('Hello, World!');
}
hello();</code></pre>
</details>
非常适合技术文档、博客中的长代码段。
五、JavaScript 交互
虽然 <details> 主打"无 JS",但配合 JavaScript 可以做更多事情。
5.1 监听展开/收起事件
javascript
const details = document.querySelector('details');
details.addEventListener('toggle', (event) => {
if (details.open) {
console.log('面板展开了');
} else {
console.log('面板收起了');
}
});
5.2 编程式控制
ini
// 展开
details.open = true;
// 收起
details.open = false;
// 切换
details.open = !details.open;
5.3 保存状态到 localStorage
ini
// 读取保存的状态
const saved = localStorage.getItem('details-open');
if (saved === 'true') {
details.open = true;
}
// 状态变化时保存
details.addEventListener('toggle', () => {
localStorage.setItem('details-open', details.open);
});
六、可访问性(A11y)
<details> 和 <summary> 原生支持键盘交互:
| 操作 | 行为 |
|---|---|
Tab |
聚焦到 <summary> |
Enter / Space |
切换展开/收起 |
| 屏幕阅读器 | 自动播报展开状态 |
无需额外 ARIA 属性,浏览器自动处理。这是它相比"手写 div + JS"方案的巨大优势。
七、浏览器兼容性
| 浏览器 | 支持情况 |
|---|---|
| Chrome | ✅ 12+ |
| Firefox | ✅ 49+ |
| Safari | ✅ 6+ |
| Edge | ✅ 79+ |
| 移动端 | ✅ 全面支持 |
这是一个极其成熟的特性,可以放心在生产环境使用。
八、与主流方案的对比
| 方案 | 是否需要 JS | 可访问性 | 代码量 | 维护成本 |
|---|---|---|---|---|
<details> + <summary> |
❌ 不需要 | ✅ 原生 | ⭐ 极少 | ⭐ 极低 |
| div + onClick | ✅ 需要 | ⚠️ 需手动处理 | ⭐⭐⭐ 较多 | ⭐⭐ 中等 |
| UI 组件库(Ant Design 等) | ✅ 依赖框架 | ✅ 已处理 | ⭐⭐ 中等 | ⭐⭐⭐ 较高 |
结论: 如果你的需求是简单的折叠/展开,用
<details>是最优雅的方案。复杂交互(如动画编排、异步加载内容)再考虑组件库。
九、总结
<details> 和 <summary> 是 HTML5 中被严重低估的"宝藏标签":
- ✅ 零 JavaScript 实现折叠面板
- ✅ 语义清晰,可访问性满分
- ✅ 浏览器原生支持,兼容性极好
- ✅ CSS 可深度定制,动画也能纯 CSS 实现
- ✅ 适合 FAQ、代码折叠、剧透隐藏等场景
下次写折叠面板时,别急着 npm install 一个组件库,先想想:能不能用三行 HTML 解决?