表格 — CodeRoadMapHTML 表格
这一课你将能做到
- 写出带 caption、thead、tbody、tfoot 的完整语义化表格,知道每个部分的渲染顺序
- 用 colspan 和 rowspan 实现跨行跨列的复杂表头,不破坏表格结构
- 用 scope 和 headers 属性建立单元格与表头的关联,让屏幕阅读器正确播报数据
为什么要学
表格是展示结构化数据的最直接方式:财务报表、课程表、对比参数、后台管理系统列表。但很多初学者把表格当成「布局工具」——用 table 做页面排版,导致代码臃肿、移动端崩溃、屏幕阅读器完全读不懂。HTML5 明确禁止表格布局,但数据表格的正确写法依然是后端管理系统、数据展示页的核心技能。
人话:表格就像 Excel 的单元格,有行有列有表头。用对了,数据一目了然;用错了,屏幕阅读器会把「价格」「100 元」读成两个孤立词,视障用户根本不知道 100 元对应什么。
先建立图像
一个完整表格的解剖结构:
┌─────────────────────────────────────┐
│ caption:表格标题(可选但推荐) │
├─────────────────────────────────────┤
│ thead:表头区域 │
│ ├─ tr:表头行 │
│ │ ├─ th:列标题 │
├─────────────────────────────────────┤
│ tbody:表体区域(可多个) │
│ ├─ tr:数据行 │
│ │ ├─ td:数据单元格 │
├─────────────────────────────────────┤
│ tfoot:表尾区域(汇总、合计) │
│ ├─ tr:汇总行 │
│ │ ├─ td 或 th │
└─────────────────────────────────────┘
关键规则:thead、tbody、tfoot 是可选的,但写了就必须按顺序出现(thead → tbody → tfoot)。tbody 可以有多个,用于分组数据。
跟着做
步骤 1:最小表格
新建 tables.html:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>表格演示</title>
</head>
<body>
<table border="1">
<tr>
<td>张三</td>
<td>85</td>
</tr>
<tr>
<td>李四</td>
<td>92</td>
</tr>
</table>
</body>
</html>
这是「能跑」的表格,但语义不完整:没有表头、没有标题、没有结构分区。改进:
<table border="1">
<caption>2026 年 10 月考试成绩</caption>
<tr>
<th>姓名</th>
<th>分数</th>
</tr>
<tr>
<td>张三</td>
<td>85</td>
</tr>
<tr>
<td>李四</td>
<td>92</td>
</tr>
</table>
caption:表格标题,默认显示在表格上方居中。
th:表头单元格,默认粗体居中。th 替代 td 放在第一行,语义化立现。
步骤 2:完整语义结构
<table border="1">
<caption>2026 年 Q3 部门预算(单位:万元)</caption>
<thead>
<tr>
<th>部门</th>
<th>Q1</th>
<th>Q2</th>
<th>Q3</th>
</tr>
</thead>
<tbody>
<tr>
<th>研发部</th>
<td>50</td>
<td>60</td>
<td>70</td>
</tr>
<tr>
<th>市场部</th>
<td>30</td>
<td>35</td>
<td>40</td>
</tr>
</tbody>
<tfoot>
<tr>
<th>合计</th>
<td>80</td>
<td>95</td>
<td>110</td>
</tr>
</tfoot>
</table>
注意:tbody 里的行首单元格也用 th,因为「研发部」「市场部」是行标题。
步骤 3:跨行跨列
<table border="1">
<caption>课程表</caption>
<thead>
<tr>
<th>时间</th>
<th>周一</th>
<th>周二</th>
<th>周三</th>
</tr>
</thead>
<tbody>
<tr>
<th>9:00-10:00</th>
<td>数学</td>
<td>英语</td>
<td>物理</td>
</tr>
<tr>
<th>10:00-11:00</th>
<td colspan="2">语文</td> <!-- 跨两列 -->
<td>化学</td>
</tr>
<tr>
<th>11:00-12:00</th>
<td rowspan="2">体育</td> <!-- 跨两行 -->
<td>生物</td>
<td>历史</td>
</tr>
<tr>
<th>14:00-15:00</th>
<!-- 这里被上一行的 rowspan 占据,不能再写 td -->
<td>地理</td>
<td>美术</td>
</tr>
</tbody>
</table>
colspan 和 rowspan 的核心规则:合并后,被合并的位置不能再写 td,否则浏览器会错位渲染。
步骤 4:scope 与 headers 建立关联
<table border="1">
<caption>员工信息表</caption>
<thead>
<tr>
<th scope="col">姓名</th>
<th scope="col">部门</th>
<th scope="col">入职年份</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">张三</th>
<td>研发部</td>
<td>2020</td>
</tr>
<tr>
<th scope="row">李四</th>
<td>市场部</td>
<td>2021</td>
</tr>
</tbody>
</table>
scope="col":该 th 是列标题,下方所有 td 都属于这一列。
scope="row":该 th 是行标题,右侧所有 td 都属于这一行。
<table border="1">
<thead>
<tr>
<th id="name">姓名</th>
<th id="q1">Q1 业绩</th>
<th id="q2">Q2 业绩</th>
</tr>
</thead>
<tbody>
<tr>
<td headers="name">张三</td>
<td headers="q1">100</td>
<td headers="q2">120</td>
</tr>
</tbody>
</table>
步骤 5:列样式与 colgroup
<table border="1">
<caption>产品参数对比</caption>
<colgroup>
<col style="background-color: #f0f0f0;"> <!-- 第一列 -->
<col span="2" style="background-color: #e0f0ff;"> <!-- 第2-3列 -->
</colgroup>
<thead>
<tr>
<th>参数</th>
<th>产品 A</th>
<th>产品 B</th>
</tr>
</thead>
<tbody>
<tr>
<th>重量</th>
<td>1.2kg</td>
<td>1.5kg</td>
</tr>
</tbody>
</table>
colgroup 和 col 用于给整列设置样式,无需在每个 td 上重复写 class。span="2" 表示该样式应用于连续两列。
讲清楚要点
- 表格只能用来展示数据,禁止布局:HTML5 规范明确表格的语义是「表格数据」。布局用 CSS Flexbox/Grid,用 table 布局会被 SEO 和无障碍工具惩罚。
- thead/tbody/tfoot 改变渲染顺序:即使 tfoot 写在 tbody 前面,浏览器也会把它渲染在表格底部。打印长表格时,thead 和 tfoot 会在每页重复。
- th 不止在第一行:行首单元格用 th + scope="row",告诉屏幕阅读器「这是行标题」。数据表格的每个维度都应有 th。
- colspan/rowspan 是「占位符」:合并后,被覆盖的 td 不能写。写多了浏览器会强制拆散,导致列数错乱。
- caption 是表格的标题,不是可选装饰:它提供上下文,让脱离正文独立出现的表格也能被理解。屏幕阅读器进入表格时首先朗读 caption。
你可能会懵的地方
-
border 属性还能用吗?
HTML5 废弃了 border 的表现层用法,但 border="1" 仍被所有浏览器支持用于快速调试。正式项目用 CSS border: 1px solid #ddd; 和 border-collapse: collapse;。
-
tbody 可以多个吗?
可以。用于分组数据,比如按月份分组的账单。每个 tbody 是一组连续的行,屏幕阅读器会分组播报。
-
td 里能放块级元素吗?
可以。td 的内容模型是 flow content,可以放 p、div、ul 甚至另一个 table(嵌套表格,但应避免,层级太深难以维护)。
-
表格宽度怎么控制?
不要用 width="100%" 属性(已废弃)。用 CSS:table { width: 100%; table-layout: fixed; }。table-layout: fixed 让列宽由第一行或 col 决定,不随内容伸缩,渲染更快。
-
空单元格怎么写?
<td></td> 或 <td> </td>。不要写 <td/>(XML 语法,HTML 不识别)。空单元格建议加 scope 或 headers 关联,或留空但保持结构完整。
错法对照
| 错法 | 问题 | 正解 |
|---|
| 用 table 做页面布局(导航+内容+侧边栏) | 语义错误、移动端灾难、SEO 差 | CSS Flexbox/Grid 布局 |
| colspan 后还多写被合并的 td | 列数错乱,浏览器强制修正 | 合并后跳过对应位置的 td |
| 所有单元格都用 td,无 th | 屏幕阅读器读不出行列关系 | 行首列首用 th,加 scope |
| 表格无 caption,只有上方一个 h3 | 表格脱离上下文后无标题 | 用 caption 作为表格自带标题 |
用 <br> 在 td 里换行做「假多行」 | 数据被切碎,无法排序筛选 | 每个逻辑行一个 tr,或用列表 |
| 深层嵌套表格(table 套 table 套 table) | 渲染性能差,维护噩梦 | 用 CSS 模拟复杂布局,数据用列表 |
卡点急救
- 表格边框线重叠成 2px:
table { border-collapse: collapse; } 合并边框。
- td 内容太长撑破表格:
table { table-layout: fixed; width: 100%; } + td { word-break: break-all; } 或 overflow: hidden; text-overflow: ellipsis;。
- 打印时每页表头不重复:确保用了 thead,浏览器打印时会自动在每页重复 thead 和 tfoot。
- rowspan 导致行高异常:被跨行的单元格内容可能只占据部分高度。给跨行单元格设
vertical-align: middle 或具体高度。
- 移动端表格横向滚动:
div.table-wrapper { overflow-x: auto; } 包裹 table,小屏幕可滑动。
面试常问
-
thead、tbody、tfoot 的作用和顺序?
thead 定义表头,tbody 定义主体数据(可多个),tfoot 定义表尾汇总。HTML 源码中顺序必须是 thead → tbody → tfoot,但浏览器渲染时 tfoot 始终显示在表格底部。打印长表格时,thead 和 tfoot 会在每页重复出现。
-
colspan 和 rowspan 的注意事项?
colspan 横向合并单元格,rowspan 纵向合并。合并后被覆盖的单元格位置不能再写 td,否则浏览器解析错乱。合并是「声明式占位」,不是视觉上的隐藏。
-
表格的 accessibility 怎么做?
① 必须有 caption 提供标题;② 表头用 th,scope 明确行/列关系;③ 复杂表头用 headers/id 关联;④ 避免用表格做布局;⑤ 空单元格保持结构完整,不随意省略 td。
-
为什么禁止表格布局?
语义错误(表格语义是数据,不是布局);可维护性差(改结构要动所有嵌套);响应式困难(表格不擅长弹性伸缩);可访问性灾难(屏幕阅读器按行列线性朗读,布局表格完全无法理解)。
自测三问
- 写出带 caption、thead、tbody、tfoot 的完整表格骨架。
- 一个单元格要跨 3 列,HTML 怎么写?被跨的位置还需要写 td 吗?
- 为什么数据表格推荐用 th + scope,而不是全部用 td?
小结一句话
表格是数据的结构化容器:caption 给标题,thead/tbody/tfoot 给分区,th 给表头,colspan/rowspan 给合并——用对了是数据利器,用错了是布局灾难。
延伸