数据表 vs 布局表
基于 HTML Living Standard · 核于 2026-06
速查
- 一条铁律:
<table>只用于数据,绝不用于页面排版或视觉布局 - 判定法:内容是否被对比 / 排序 / 计算 / 交叉引用?是数据才用表,否则是布局
- 为什么别用表布局:读屏会把它当数据表念(报行列表头),结构语义全错、且难以响应式
- 万不得已用了表来排版,必须加
role="presentation"(同义词role="none")去掉表格语义 role="presentation"会连带让<caption>/<thead>/<tbody>/<tfoot>/<tr>/<th>/<td>一起退出无障碍树- 但
<th>/<td>内部的内容(含链接、嵌套表)仍照常暴露给辅助技术 - 表单排版也别用表格——交给 CSS Grid / Flexbox(详见表单一叶)
- 表格默认不响应式:它按内容撑宽。常见三招——外层容器横向滚动、
display:block+滚动、断点重排为卡片 - 改
display做响应式会破坏无障碍树,须补回role(见上一页)
一条铁律:表格只装数据
现行标准与 web.dev 的态度毫不含糊:<table> 不能用于呈现/排版,只能用于数据。一个朴素的判据——「如果这份内容得用电子表格(spreadsheet)才能在会上讲清楚,那它才该用 <table>」。换言之,要被对比、排序、计算、交叉引用的二维信息(成绩单、价目表、对账单)属于数据;而「把页面分成几栏」「把缩略图排成网格」属于布局,与表格无关。
为什么不能拿表格排版
在 CSS 普及之前,开发者曾用嵌套 <table> 拼整个页面布局。今天这是明确的反模式,原因有三:
- 语义错乱:
<table>的隐式角色是table,读屏会把它当数据表处理——尝试播报行表头、列表头、单元格坐标。可布局表根本没有「表头」「行列含义」,用户听到的是一堆无意义的导航提示,体验灾难。 - 难以响应式:表格按内容撑宽、列宽相互牵制,天生不适应窄屏;而布局本就该随视口流动,用 Grid / Flexbox 几行就能搞定,用表格则处处别扭。
- 维护沉重:嵌套表布局的 HTML 又深又脆,改一处牵一片,可读性远差于语义化结构 + CSS。
正确的布局工具是 CSS Grid(二维网格布局)、Flexbox(一维弹性布局)、多列布局(columns,适合分栏文本)。需要「标题压在内容上方」用标题元素 + 列表,需要「术语—释义」用描述列表 <dl>。表单的排版同样不该用表格,交给 CSS 布局即可(见表单一叶)。
实在要用:role="presentation"
如果由于历史包袱,某处确实用 <table> 做了纯展示/排版,规范要求给它加 role="presentation"(或其同义词 role="none"),移除表格的隐式语义,免得读屏把它当数据表念。
<table role="presentation">
<tr>
<td><!-- 仅用于排版的左栏 --></td>
<td><!-- 仅用于排版的右栏 --></td>
</tr>
</table>它的级联行为很关键:
role="presentation"/none加在<table>上时,会连带让后代的<caption>/<thead>/<tbody>/<tfoot>/<tr>/<th>/<td>一起继承该角色,统统退出无障碍树;- 但
<th>/<td>内部的内容(包括链接、表单控件、乃至嵌套表格)仍照常暴露给辅助技术——被去掉的只是表格容器这层语义,内容本身不受影响。
role="presentation" 的元素不要再起名
被标为 presentation / none 的元素已不在无障碍树里,不应再有可访问名称——别给它加 aria-label 或 aria-labelledby。另外,浏览器会忽略加在可聚焦元素(链接、表单控件、带 tabindex 的元素)上的 presentation / none,这类元素的角色仍会暴露。
说到底,
role="presentation"是给「拆不掉的历史布局表」兜底用的创可贴。新代码不该走到这一步——一开始就用 CSS 布局,根本不需要它。
数据表的响应式策略
即便是正经数据表,也得面对一个事实:表格默认不响应式——它按内容固有尺寸撑开,不会自动适配从手机到 4K 的各种屏。窄屏上要么撑破容器、要么被压到不可读。需要额外手段,常见三招:
1. 外层容器横向滚动(最稳)
把 <table> 包进一个可横向滚动的容器,表格本身结构不动,窄屏时整表可左右滑动。这招不破坏表格语义,是最稳妥的方案:
<div class="table-scroll" tabindex="0" role="region" aria-label="季度营收表">
<table>…</table>
</div>.table-scroll {
overflow-x: auto;
}给滚动容器加 tabindex="0" + role="region" + aria-label,键盘用户也能聚焦并滚动它。
2. display: block + 滚动 / 粘性表头
直接让表格自身可滚,并把表头用 position: sticky 钉住,滚动时表头不跑:
table {
display: block;
overflow: auto;
max-width: 100%;
height: 240px;
}
th {
position: sticky;
top: 0;
background: #fff;
}改 display 必补 role
一旦把 <table> 改成 display: block(或把行改成 flex),表格的隐式 ARIA 语义就被打破,必须补回 role="table" / rowgroup / row / columnheader / rowheader / cell(详见 表格可访问性)。否则视觉照常,读屏却读不出行列关系。
3. 断点重排为「卡片」
在很窄的屏上,用 CSS 把每一行重排成一张「字段名—值」的卡片(常借 data-* 属性 + ::before 把列名显示在每个值前面)。视觉体验好,但最易破坏无障碍(动了 display),务必连带补 role 并实测读屏。
小结
<table> 是数据的专属容器——被对比/排序/计算/交叉引用的二维信息才用它,布局一律交给 CSS Grid / Flexbox;历史遗留的布局表须加 role="presentation" 摘掉语义。数据表本身不响应式,优先用「外层容器横向滚动」这种不动语义的方案;若改 display,记得补 role。至此六页深度内容走完,元素清单、scope/headers 速查与权威链接汇总见 参考。