原生 details/summary 可访问折叠面板实战

举报
yd_229231784 发表于 2026/11/12 10:01:43 2026/11/12
【摘要】 原生 details/summary 已提供展开状态、焦点与 Enter/Space 切换,本文用最小 HTML、CSS 和可选互斥脚本演示折叠面板,并说明 summary 内不要放按钮的原因、边界与读屏验证步骤。

原生 details/summary 可访问折叠面板实战

问题场景

FAQ、设置项或说明区常需要折叠面板。用 div 加点击事件实现时,开发者还要自己维护 aria-expanded、tabindex、焦点样式以及 Enter/Space 键行为,容易遗漏。原生 details 与 summary 已经把“展开/折叠”交互交给浏览器:summary 可获得焦点,按 Enter 或空格切换,辅助技术也能读取展开状态。

本文给出一份可直接运行的最小示例,并重点说明一个常见误区:不要在 summary 内再放 button、a 或 input。

为什么 summary 内不要再放按钮

反例通常写成这样:

<details>
  <summary>
    <span>面板标题</span>
    <button type='button'>复制</button>
  </summary>
  <p>面板内容。</p>
</details>

问题不在于“按钮不能点击”,而在于 summary 本身已经是负责展开/折叠的交互控件。把按钮嵌进去后会出现三类歧义:

  1. 点击按钮时,事件可能冒泡到 summary,导致用户想执行按钮动作,却意外触发面板折叠。
  2. 键盘焦点落在内部按钮上时,Enter/Space 应激活按钮,而不是切换 summary,用户和读屏软件都难以判断当前操作对象。
  3. 无障碍树中会出现嵌套的交互控件,摘要的“可展开”语义被削弱,读屏播报也更冗长。

如果确实需要“复制”“删除”等操作,应把按钮放到 summary 外;若操作只对展开后的内容生效,可放进 details 的内容区,但要接受折叠时该操作不可见。

最小可用示例

将下面内容保存为 index.html,无需构建工具。示例使用占位标题和内容,可按实际业务替换。

<!doctype html>
<html lang='zh-CN'>
<head>
  <meta charset='utf-8'>
  <meta name='viewport' content='width=device-width, initial-scale=1'>
  <title>原生折叠面板示例</title>
  <style>
    * { box-sizing: border-box; }
    body {
      margin: 0;
      font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
      color: #1f2328;
      background: #f6f8fa;
    }
    .wrap {
      max-width: 720px;
      margin: 40px auto;
      padding: 0 16px;
    }
    .panel {
      margin: 12px 0;
      background: #fff;
      border: 1px solid #d0d7de;
      border-radius: 8px;
    }
    .panel summary {
      padding: 14px 16px;
      font-weight: 600;
      cursor: pointer;
    }
    .panel summary:focus-visible {
      outline: 3px solid #005fcc;
      outline-offset: 2px;
    }
    .panel[open] summary {
      border-bottom: 1px solid #e5e7eb;
    }
    .panel summary::marker {
      color: #005fcc;
    }
    .panel .content {
      padding: 12px 16px;
      line-height: 1.7;
    }
  </style>
</head>
<body>
  <main class='wrap'>
    <h1>常见问题</h1>

    <details class='panel'>
      <summary>如何展开这个面板?</summary>
      <div class='content'>
        <p>用 Tab 将焦点移到摘要上,按 Enter 或空格切换。</p>
      </div>
    </details>

    <details class='panel'>
      <summary>折叠后内容还能被键盘访问吗?</summary>
      <div class='content'>
        <p>默认关闭时内容不显示,键盘和读屏浏览通常也不会进入其中。</p>
      </div>
    </details>

    <details class='panel'>
      <summary>如何设置默认展开?</summary>
      <div class='content'>
        <p>在 details 上添加 open 属性即可,例如 details class='panel' open。</p>
      </div>
    </details>
  </main>
</body>
</html>

以上三个面板彼此独立。若需要“每次只展开一个”,可以增加可选脚本,不要拦截 summary 的点击或键盘事件:

<script>
  const panels = Array.from(document.querySelectorAll('details.panel'));
  panels.forEach((panel) => {
    panel.addEventListener('toggle', () => {
      if (!panel.open) return;
      panels.forEach((other) => {
        if (other !== panel) other.open = false;
      });
    });
  });
</script>

这段脚本只监听原生 toggle 事件,展开一个面板时关闭其他面板,不改变 Enter/Space 的默认行为。

运行与验证方法

保存文件后用浏览器打开,按以下顺序检查:

  1. 按 Tab 键,确认焦点能移到每个 summary,并且焦点轮廓清晰可见。
  2. 在 summary 上按 Enter 和空格,确认面板展开、折叠状态正确切换。
  3. 用鼠标点击摘要文字,确认只切换一次,不会因事件冒泡重复切换。
  4. 在浏览器开发者工具的无障碍面板中查看 summary,确认其可访问名称来自摘要文字,展开状态随操作变化。
  5. 用读屏软件按 Tab 聚焦摘要,确认能听到标题及折叠或展开状态;激活后检查内容可读,折叠后内容不再进入浏览顺序。不同浏览器与读屏组合的播报措辞可能不同,应以目标环境实测为准。

边界与限制

  • details 的内容应放在 summary 之后;不要把 summary 当成普通容器塞入多个交互控件。
  • 不要为了美观直接删除焦点轮廓;可以自定义 :focus-visible 样式,但要保持明显可见。
  • 默认的展开/折叠是即时完成的。若要做高度动画,需要额外包装和脚本,并重新验证关闭状态下的焦点与读屏行为。
  • 自定义 summary 标记时,保留可点击区域和状态提示,避免只靠颜色表达展开状态。
  • 互斥脚本属于渐进增强;禁用脚本时,多个面板仍可独立使用。
  • 若业务要求复杂菜单、嵌套弹层或摘要内操作按钮,应重新评估组件结构,而不是把交互压力都放进 summary。
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。