展开收起按钮必须使用元素,因其自带可聚焦、键盘响应及role="button"语义;禁用或,否则违反可访问性规范。

展开收起按钮必须用 <button></button>,不能用 <div> 或 <code>@#@#@#@#@#@#@#@#@#@0 —— 这两类都缺失交互语义,Lighthouse 会报 “Interactive element not focusable” 错误。
- 必须为
<button type="button"></button>,避免表单提交干扰
- 按钮文本需动态更新(“展开”/“收起”),不能靠 CSS 伪元素生成,否则读屏器无法播报变化
- 若内容区有标题,建议用
aria-expanded 同步控制状态,例如:<button aria-expanded="false">展开</button>
<details></details> 是最简语义方案,但兼容性与样式受限
<button type="button"></button>,避免表单提交干扰aria-expanded 同步控制状态,例如:<button aria-expanded="false">展开</button>
<details></details> 是最简语义方案,但兼容性与样式受限原生 <details></details> + <summary></summary> 标签天然支持展开收起,且自带 role="group"、aria-expanded 和键盘操作(空格/回车切换),无需 JS 就能工作。但它有两个硬约束:
- IE 完全不支持,Edge 18– 及旧版 Safari 需 polyfill;如项目需兼容 IE,直接放弃
-
<summary></summary>渲染为内联元素,且强制前置三角符号,无法完全移除或重定位(比如“紧随文案末尾”这种布局) - 无法对展开区域做高度过渡动画(
height: auto不支持 CSS transition),只能靠max-height拼凑,易出现文字截断或滚动条闪动
示例合法结构:<details><summary>点击展开</summary><p>长文本…</p></details>。注意:里面不能放 <h2></h2> 等标题,否则会破坏大纲层级。
自定义实现时,<section></section> 必须带显式标题
如果不用 <details></details>,而用 <div id="content"> 包裹展开内容,这个容器不能裸写——它必须包裹在语义化区块中,且该区块要有自己的标题(<code><h2></h2>–<h6></h6>)。否则,<section></section> 和 <article></article> 在语义上等价于 <div>,还会污染文档大纲。
<ul>
<li>错误:<code><div class="toggle-content">...</div> —— 无任何语义,读屏器无法告知用户这是什么内容区块
<section><h3>详细说明</h3>
<div class="toggle-content">...</div></section>
<aside></aside> 并配 <h4></h4>,而非 <section></section>
隐藏逻辑必须用 aria-hidden + hidden 双保险
仅靠 display: none 或 visibility: hidden 隐藏内容,会导致屏幕阅读器仍可能读取(尤其当用 JS 切换 class 时,部分读屏器缓存未及时刷新)。正确做法是同时控制两个属性:
- 初始状态:内容区加
hidden属性(HTML 原生属性,语义明确)和aria-hidden="true" - 展开时:移除
hidden,设aria-hidden="false" - 收起时:加回
hidden,设aria-hidden="true" - 切勿只依赖 CSS 类名控制可访问性状态,JS 必须同步更新 ARIA 属性
示例 JS 片段:textContent.hidden = !expanded; textContent.setAttribute('aria-hidden', String(!expanded));
真正难的不是让文字消失,而是让辅助技术、搜索引擎、键盘用户和视觉用户在同一时刻获得一致的信息状态。所有样式、JS 和标签选择,最终都要回归到“这个元素此刻在文档中扮演什么角色”这一判断上。











