在前端开发中,折叠与展开(Accordion)是一种极其常见的交互模式,广泛出现在 FAQ 页面、侧边栏导航、移动端菜单等场景中。过去,我们通常依赖 JavaScript 甚至第三方库来实现这一效果。然而,HTML5 早已为我们提供了一个原生解决方案:<details> 元素。

它不仅语义清晰、开箱即用,还天然支持无障碍访问。配合 CSS 的样式定制能力,几乎可以覆盖大多数折叠交互需求。本文将带你从入门到精通,彻底掌握 <details> 的使用与进阶技巧。

一、基础认知

1.1 元素组成

<details> 元素由两部分组成:

元素

角色

说明

<summary>

标题区

始终可见,点击后触发展开或收起

其余子元素

内容区

折叠状态下隐藏,展开后显示

<details>
  <summary>什么是 Flexbox?</summary>
  <p>Flexbox 是 CSS3 中的一种一维布局模型,用于在容器内对子元素进行排列、对齐和分配空间。</p>
</details>

如果省略 <summary>,浏览器会自动生成一个默认文本(通常为 "Details" 或 "详细信息")。

1.2 open 属性

open 是 <details> 唯一的布尔属性,用于控制初始状态:

<!-- 默认收起 -->
<details>
  <summary>收起状态</summary>
  <p>需要点击才能看到。</p>
</details>

<!-- 默认展开 -->
<details open>
  <summary>展开状态</summary>
  <p>页面加载后直接可见。</p>
</details>
1.3 底层原理

浏览器对 <details> 的默认渲染行为如下:

/* 浏览器 UA 样式表中的默认规则 */
details > *:not(summary) {
  display: none; /* 内容默认隐藏 */
}

details[open] > *:not(summary) {
  display: block; /* 展开时显示 */
}

summary {
  display: list-item; /* 作为列表项渲染 */
  list-style: disclosure-closed; /* 显示三角箭头 */
}

details[open] > summary {
  list-style: disclosure-open; /* 展开时箭头方向改变 */
}

由此可见,折叠与展开的核心机制是 CSS display 属性的切换,内容始终存在于 DOM 中,不会因折叠而被移除。

二、CSS 深度样式定制

2.1 基础美化

通过 CSS 可以轻松美化 <details> 的外观:

details {
  border: 1px solid #e2e8f0;
  border-radius: 8px;
  padding: 12px 16px;
  margin-bottom: 12px;
  background-color: #f8fafc;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
  transition: background-color 0.2s;
}

details:hover {
  background-color: #f1f5f9;
}

details[open] {
  background-color: #ffffff;
  border-color: #3b82f6;
}

summary {
  font-weight: 600;
  font-size: 16px;
  color: #1e293b;
  cursor: pointer;
  padding: 4px 0;
  user-select: none; /* 防止双击选中文字 */
}

summary:focus-visible {
  outline: 2px solid #3b82f6;
  outline-offset: 2px;
  border-radius: 4px;
}

2.2 自定义三角箭头

默认的三角箭头样式可以通过 ::marker 伪元素或完全自定义的方式覆盖:

方案一:修改 marker 内容
/* 方案一:修改 marker 内容 */
summary::marker {
  content: '📌 ';
}

details[open] > summary::marker {
  content: '📂 ';
}

方案二:完全自定义
summary {
  list-style: none; /* 标准方式移除 */
}

summary::-webkit-details-marker {
  display: none; /* WebKit 浏览器兼容 */
}

summary {
  position: relative;
  padding-left: 24px;
}

summary::before {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 0;
  height: 0;
  border-left: 6px solid #64748b;
  border-top: 5px solid transparent;
  border-bottom: 5px solid transparent;
  transition: transform 0.25s ease;
}

details[open] > summary::before {
  transform: translateY(-50%) rotate(90deg);
}

2.3 展开与收起动画

原生 <details> 不支持高度过渡动画,以下是三种主流解决方案:

方案 A:max-height 过渡(纯 CSS)
details .content-wrapper {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.35s ease, padding 0.35s ease;
  padding: 0 16px;
}

details[open] .content-wrapper {
  max-height: 800px; /* 需要预估一个足够大的值 */
  padding: 12px 16px;
}

<details>
  <summary>带动画的折叠</summary>
  <div class="content-wrapper">
    <p>内容会平滑展开。</p>
  </div>
</details>

缺点:max-height 值需要预估,过大会导致动画速度不均匀。

方案 B:grid 行高过渡(纯 CSS,更优雅)
details .content-wrapper {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.35s ease;
  overflow: hidden;
}

details[open] .content-wrapper {
  grid-template-rows: 1fr;
}

details .content-wrapper > .inner {
  min-height: 0;
}

<details>
  <summary>Grid 动画方案</summary>
  <div class="content-wrapper">
    <div class="inner">
      <p>利用 grid-template-rows 从 0fr 到 1fr 的过渡,实现真正的高度动画。</p>
    </div>
  </div>
</details>

这是目前纯 CSS 中最推荐的方案,无需预估高度。

方案 C:interpolate-size(实验性,未来标准)
:root {
  interpolate-size: allow-keywords;
}

details .content-wrapper {
  height: 0;
  overflow: hidden;
  transition: height 0.35s ease;
}

details[open] .content-wrapper {
  height: auto;
}

这是 CSS 正在推进的新特性,允许 height: 0 到 height: auto 的直接过渡。Chrome 129+ 已支持。

三、name 属性——纯 CSS 手风琴效果

HTML 规范新增的 name 属性让同名 <details> 元素互斥,打开一个时会自动关闭其他:

<div class="accordion">
  <details name="faq" open>
    <summary>Postman 是什么?</summary>
    <div class="content-wrapper">
      <div class="inner">
        <p>Postman 是一个全功能的 API 开发平台,支持 API 的设计、测试、文档、监控等全生命周期管理。</p>
      </div>
    </div>
  </details>

  <details name="faq">
    <summary>什么是 Collection?</summary>
    <div class="content-wrapper">
      ...
    </div>
  </details>

  ...
</div>

.accordion {
  max-width: 600px;
  margin: 0 auto;
}

.accordion details {
  border: 1px solid #e2e8f0;
  border-radius: 0;
  margin-bottom: -1px; /* 合并边框 */
}

.accordion details:first-child {
  border-radius: 8px 8px 0 0;
}

.accordion details:last-child {
  border-radius: 0 0 8px 8px;
}

浏览器支持: Chrome 120+、Edge 120+、Safari 17.2+、Firefox 130+。

四、JavaScript 增强

4.1 toggle 事件
document.querySelectorAll('details').forEach(detail => {
  detail.addEventListener('toggle', (e) => {
    const isOpen = detail.open;
    const summaryText = detail.querySelector('summary').textContent;
    console.log(`"${summaryText}" ${isOpen ? '已展开' : '已收起'}`);
    // 可用于埋点统计
    analytics.track('details_toggle', {
      section: summaryText,
      action: isOpen ? 'expand' : 'collapse'
    });
  });
});
4.2 程序化控制
const details = document.querySelector('details');

// 展开
details.open = true;
details.setAttribute('open', '');

// 收起
details.open = false;
details.removeAttribute('open');

// 切换
details.open = !details.open;
4.3 全部展开 / 收起
function toggleAll(expand) {
  document.querySelectorAll('details').forEach(d => {
    d.open = expand;
  });
}

// 使用
toggleAll(true);  // 全部展开
toggleAll(false); // 全部收起

五、无障碍(Accessibility)

<details> 天然具备良好的无障碍支持:

特性

说明

键盘操作

Enter / Space 键切换展开与收起

Tab 导航

<summary> 默认可聚焦

ARIA 角色

浏览器自动为 <summary> 添加 role="button" 和 aria-expanded

屏幕阅读器

正确朗读展开与收起状态

最佳实践:
<!-- 为内容区添加 role 和 aria-label 增强语义 -->
<details>
  <summary aria-label="展开查看 API 认证说明">API 认证</summary>
  <div role="region" aria-label="API 认证详细说明">
    <p>使用 Bearer Token 进行身份验证...</p>
  </div>
</details>

六、实战场景

6.1 FAQ 页面
<section class="faq">
  <h2>常见问题</h2>
  <details name="faq">
    <summary>如何重置密码?</summary>
    <p>前往设置页面,点击"安全"选项卡,选择"修改密码"。</p>
  </details>
  <details name="faq">
    <summary>支持哪些支付方式?</summary>
    <p>支持微信支付、支付宝、银行卡等主流支付方式。</p>
  </details>
</section>
6.2 代码片段展示
<details>
  <summary>查看完整代码</summary>
  <pre><code>
function debounce(fn, delay) {
  let timer = null;
  return function(...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
}
  </code></pre>
</details>
6.3 移动端纯 CSS 导航菜单
<nav>
  <details>
    <summary class="menu-btn">☰ 菜单</summary>
    <ul class="nav-links">
      <li><a href="/">首页</a></li>
      <li><a href="/about">关于</a></li>
      <li><a href="/contact">联系我们</a></li>
    </ul>
  </details>
</nav>

.menu-btn {
  font-size: 18px;
  padding: 12px;
  background: #1e293b;
  color: white;
  list-style: none;
}

.nav-links {
  list-style: none;
  padding: 0;
  background: #334155;
}

.nav-links li a {
  display: block;
  padding: 12px 16px;
  color: white;
  text-decoration: none;
}

.nav-links li a:hover {
  background: #475569;
}
6.4 调试信息面板
<details>
  <summary>🔍 调试信息(点击展开)</summary>
  <table>
    <tr><td>请求方法</td><td>POST</td></tr>
    <tr><td>状态码</td><td>200 OK</td></tr>
    <tr><td>响应时间</td><td>142ms</td></tr>
    <tr><td>Content-Type</td><td>application/json</td></tr>
  </table>
</details>

七、浏览器兼容性

特性

Chrome

Firefox

Safari

Edge

<details>/<summary>

12+

49+

6+

79+

name属性(互斥)

120+

130+

17.2+

120+

::marker伪元素

86+

68+

11.1+

86+

interpolate-size

129+

129+

建议访问《Can I Use》网站,以及时了解 details 属性在各类浏览器中的最新兼容性支持情况。

总结

<details> 是一个被严重低估的 HTML 原生元素,它的核心优势在于:

  1. 零依赖:无需 JavaScript 即可实现基础的折叠与展开交互。

  2. 语义化:浏览器和搜索引擎能正确理解内容结构。

  3. 无障碍友好:天然支持键盘导航和屏幕阅读器。

  4. 高度可定制:通过 CSS 可以完全改变外观,包括箭头、动画、布局  。

  5. 互斥手风琴 —— name 属性让纯 CSS 手风琴成为现实。

在实际项目中,建议优先考虑 <details> 来实现折叠交互,只有在需要复杂动画或特殊交互逻辑时才引入 JavaScript 增强。随着 interpolate-size 等新特性的推进,<details> 的动画短板也将被彻底补齐。

彩蛋

文中配有完整示例,点击《HTML <details> 元素:完整交互示例》即可查看。