不是。多级表头应通过 thead 中多行 tr 配合 th 的 colspan、rowspan 控制跨格,并统一使用 scope="col"(95%场景),仅当逻辑列组需明确语义时才考虑 scope="colgroup";嵌套 th 或滥用 scope 均会导致无障碍失效。

多级表头必须用 colgroup + col 配合 th 的 scope 吗?
不是。纯靠嵌套 th 元素(比如 th 里再放 th)无法正确表达多级语义,scope 属性也只接受 row、col、rowgroup、colgroup 四个值,不支持“二级列头”这类抽象层级。真正起作用的是 HTML 表格的结构嵌套逻辑:用 thead 中多行 tr,配合 th 的 colspan 和 rowspan 控制跨格,再辅以 scope 明确每个表头的关联方向。
scope="col" 和 scope="colgroup" 到底该选哪个?
绝大多数情况下用 scope="col"。只有当某表头单元格通过 colspan 覆盖了多个连续列,且这些列在逻辑上属于同一组(比如“Q1 销售数据”下含“线上”、“线下”两列),才考虑用 scope="colgroup"。但注意:scope="colgroup" 不会自动绑定到 colgroup 元素——它只是语义声明,辅助技术(如读屏器)推断范围,浏览器并不校验是否真有对应 colgroup。实际中,95% 的多级表头场景,老老实实用 scope="col" + 合理的 rowspan/colspan 就够了。
常见错误:合并单元格后 scope 失效或读屏器乱读
问题往往出在结构断裂:比如第二行 th 用了 colspan="2",但第一行没留出对应空位,导致列数错位;或者 th 缺少 scope 属性,仅靠视觉对齐误导开发者。读屏器依赖表格的 DOM 列序推导关系,一旦 tr 内 th/td 数量不一致(又没用 rowspan/colspan 正确声明),就会跳列或重复播报。
- 确保每行
tr在“逻辑列数”上一致(用colspan和rowspan声明,而非删减元素) - 顶层表头(最上一行)的每个
th必须带scope="col",哪怕它colspan了 - 中间层级表头(如分组标题)若横向覆盖多列,用
scope="col"即可,不要强行改colgroup - 避免在
th里嵌套表格或div—— 这会切断表格语义流
一个能过无障碍检测的双层表头示例
下面是一个带“部门 / 季度”两级结构的表格头部,兼容主流读屏器:
<table>
<thead>
<tr>
<th scope="col" rowspan="2">员工姓名</th>
<th scope="col" colspan="2">技术部</th>
<th scope="col" colspan="2">市场部</th>
</tr>
<tr>
<th scope="col">Q1</th>
<th scope="col">Q2</th>
<th scope="col">Q1</th>
<th scope="col">Q2</th>
</tr>
</thead>
<tbody>
<tr><td>张三</td><td>85</td><td>92</td><td>78</td><td>88</td></tr>
</tbody>
</table>
关键点:第一行用 rowspan="2" 让“员工姓名”贯穿两行;用 colspan="2" 声明“技术部”管后面两列;第二行补全所有叶子列头,并全部带上 scope="col"。这样读屏器读每一数据单元格时,能准确回溯到对应的部门和季度。
最易被忽略的是:即使视觉上“技术部”横跨两列,它的 scope 仍应是 col,不是 colgroup;而它的子列头(Q1/Q2)必须存在且带 scope,不能靠 CSS 隐藏或 JS 动态注入。

















