Skip to content

数据表 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> 拼整个页面布局。今天这是明确的反模式,原因有三:

  1. 语义错乱<table> 的隐式角色是 table,读屏会把它当数据表处理——尝试播报行表头、列表头、单元格坐标。可布局表根本没有「表头」「行列含义」,用户听到的是一堆无意义的导航提示,体验灾难。
  2. 难以响应式:表格按内容撑宽、列宽相互牵制,天生不适应窄屏;而布局本就该随视口流动,用 Grid / Flexbox 几行就能搞定,用表格则处处别扭。
  3. 维护沉重:嵌套表布局的 HTML 又深又脆,改一处牵一片,可读性远差于语义化结构 + CSS。

正确的布局工具是 CSS Grid(二维网格布局)、Flexbox(一维弹性布局)、多列布局columns,适合分栏文本)。需要「标题压在内容上方」用标题元素 + 列表,需要「术语—释义」用描述列表 <dl>表单的排版同样不该用表格,交给 CSS 布局即可(见表单一叶)。

实在要用:role="presentation"

如果由于历史包袱,某处确实用 <table> 做了纯展示/排版,规范要求给它加 role="presentation"(或其同义词 role="none"),移除表格的隐式语义,免得读屏把它当数据表念。

html
<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-labelaria-labelledby。另外,浏览器会忽略加在可聚焦元素(链接、表单控件、带 tabindex 的元素)上的 presentation / none,这类元素的角色仍会暴露。

说到底,role="presentation" 是给「拆不掉的历史布局表」兜底用的创可贴。新代码不该走到这一步——一开始就用 CSS 布局,根本不需要它。

数据表的响应式策略

即便是正经数据表,也得面对一个事实:表格默认不响应式——它按内容固有尺寸撑开,不会自动适配从手机到 4K 的各种屏。窄屏上要么撑破容器、要么被压到不可读。需要额外手段,常见三招:

1. 外层容器横向滚动(最稳)

<table> 包进一个可横向滚动的容器,表格本身结构不动,窄屏时整表可左右滑动。这招不破坏表格语义,是最稳妥的方案:

html
<div class="table-scroll" tabindex="0" role="region" aria-label="季度营收表">
  <table>…</table>
</div>
css
.table-scroll {
  overflow-x: auto;
}

给滚动容器加 tabindex="0" + role="region" + aria-label,键盘用户也能聚焦并滚动它。

2. display: block + 滚动 / 粘性表头

直接让表格自身可滚,并把表头用 position: sticky 钉住,滚动时表头不跑:

css
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 速查与权威链接汇总见 参考