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

展开收起按钮必须用 <button>,不能用 <div> 或 <a>
用 <div> 或 <a href="#"> 实现切换,会导致键盘不可聚焦、屏幕阅读器无法识别交互意图、按回车键无响应。浏览器默认的 <button> 自带 role="button"、可聚焦、支持空格/回车触发,是唯一符合语义的控制元素。
常见错误写法:<div onclick="toggle()">展开</div> 或 <a href="javascript:void(0)">展开</a> —— 这两类都缺失交互语义,Lighthouse 会报 “Interactive element not focusable” 错误。
- 必须为
<button type="button">,避免表单提交干扰 - 按钮文本需动态更新(“展开”/“收起”),不能靠 CSS 伪元素生成,否则读屏器无法播报变化
- 若内容区有标题,建议用
aria-expanded同步控制状态,例如:<button aria-expanded="false">展开</button>
<details> 是最简语义方案,但兼容性与样式受限
原生 <details> + <summary> 标签天然支持展开收起,且自带 role="group"、aria-expanded 和键盘操作(空格/回车切换),无需 JS 就能工作。但它有两个硬约束:
- IE 完全不支持,Edge 18– 及旧版 Safari 需 polyfill;如项目需兼容 IE,直接放弃
-
<summary>渲染为内联元素,且强制前置三角符号,无法完全移除或重定位(比如“紧随文案末尾”这种布局) - 无法对展开区域做高度过渡动画(
height: auto不支持 CSS transition),只能靠max-height拼凑,易出现文字截断或滚动条闪动
示例合法结构:<details><summary>点击展开</summary><p>长文本…</p></details>。注意:里面不能放 <h2> 等标题,否则会破坏大纲层级。
立即学习“前端免费学习笔记(深入)”;
自定义实现时,<section> 必须带显式标题
如果不用 <details>,而用 <div id="content"> 包裹展开内容,这个容器不能裸写——它必须包裹在语义化区块中,且该区块要有自己的标题(<h2>–<h6>)。否则,<section> 和 <article> 在语义上等价于 <div>,还会污染文档大纲。
- 错误:
<div class="toggle-content">...</div>—— 无任何语义,读屏器无法告知用户这是什么内容区块 - 正确:
<section><h3>详细说明</h3><div class="toggle-content">...</div></section> - 标题要真实反映内容主题,不能写“点击展开”,那是行为描述,不是内容主题
- 如果该段落本身就是文章主体的一部分(如博客正文中的补充说明),优先用
<aside>并配<h4>,而非<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 和标签选择,最终都要回归到“这个元素此刻在文档中扮演什么角色”这一判断上。



















