<th>不能仅靠视觉加粗标识表头,因其核心作用是语义标注而非样式;必须配合scope属性(col/row)明确作用范围,否则屏幕阅读器和排序插件无法识别结构。

为什么 <th> 不能只靠视觉加粗来标识表头
很多初学者用 <th> 单纯为了文字加粗或居中,结果表格在屏幕阅读器里读不出结构,排序插件也识别不了哪列是“姓名”、哪列是“年龄”。<th> 的本质作用是语义标注——告诉浏览器和辅助技术“这一格定义了它下方/右方数据的类别”。没写对 scope 或没嵌套在正确上下文(如 <thead>),就等于白标。
<th> 必须配 scope 属性才真正生效
单靠 <th> 标签本身,无法自动推断它管的是哪一行或哪一列。必须显式声明作用范围:
-
scope="col":表示该表头控制整列(最常见,用于列名,如“订单号”“下单时间”) -
scope="row":表示该表头控制整行(用于行名,如“小明”“张工”,多见于二维汇总表) - 不写
scope,或写成scope="auto"(无效值),多数辅助技术会忽略其表头身份
示例:
<table>
<thead>
<tr>
<th scope="col">产品</th>
<th scope="col">销量</th>
<th scope="col">单价</th>
</tr>
</thead>
<tbody>
<tr>
<td>鼠标</td>
<td>120</td>
<td>¥89</td>
</tr>
</tbody>
</table>
多级表头要用 <colgroup> + colspan/rowspan 配合 scope
当出现“2024年销售”下分“Q1”“Q2”这种嵌套分类时,仅靠 <th> 堆叠会丢失语义关联。必须分层处理:
- 顶层
<th>用colspan="2"覆盖子列,并设scope="col" - 子层
<th>各自设scope="col",且不跨列 - 必要时用
<colgroup>显式定义列组,增强可访问性支持
错误写法:<th><th>Q1</th><th>Q2</th></th>(嵌套无效)
正确结构需用两行 <tr> 实现分层,每层 <th> 独立带 scope。
React/Vue 中动态渲染 <th> 容易漏掉 scope
模板里写死的 <th scope="col"> 没问题,但用 v-for 或 {columns.map()} 渲染表头时,开发者常只传 label,忘了同步传 scope 类型:
- 列头统一用
scope="col";行头(如首列姓名)用scope="row" - 不要依赖 CSS 类名判断方向,JSX/模板里必须硬编码或从配置项透传
scope - 服务端渲染(SSR)场景下,缺失
scope会导致首屏无障碍支持直接失效
Vue 示例:
<th v-for="col in columns" :key="col.key" :scope="col.scope || 'col'">{{ col.label }}</th>
表头语义不是“写了就行”,而是“写对了才能被正确理解”。scope 属性漏掉、<th> 放错位置(比如塞进 <tbody>)、或者用 <td> 加样式冒充表头——这三类问题在线上表格中高频出现,且很难被肉眼发现。


















