跳到主要内容

HTML Table 表格结构

表格是 HTML 中用于展示结构化数据的核心元素。虽然现代布局已不推荐用表格实现,但在数据展示、报表呈现等场景中,语义化的表格依然不可替代。

表格的基本组成

一个完整的 HTML 表格由以下核心标签构成:

标签说明是否必需
<table>表格的根容器
<caption>表格标题,紧跟 <table> 之后否(推荐)
<thead>表头分组否(推荐)
<tbody>表体分组否(推荐)
<tfoot>表尾分组
<tr>表格行(table row)
<th>表头单元格(table header)
<td>数据单元格(table data)
<colgroup> / <col>列分组与列样式控制

最简表格

最基础的表格只需要 <table><tr><td> 三个标签:

<table>
<tr>
<td>姓名</td>
<td>年龄</td>
</tr>
<tr>
<td>小明</td>
<td>18</td>
</tr>
</table>

渲染效果:

姓名年龄
小明18
注意

虽然浏览器允许省略 <thead><tbody>,但缺少语义分组的表格不利于无障碍访问和样式控制。实际开发中应始终使用完整的语义结构。

语义化完整表格

一个规范的语义化表格示例:

<table>
<caption>2024 年第一季度销售数据</caption>
<thead>
<tr>
<th>月份</th>
<th>销售额(万元)</th>
<th>同比增长</th>
</tr>
</thead>
<tbody>
<tr>
<td>1 月</td>
<td>320</td>
<td>+12%</td>
</tr>
<tr>
<td>2 月</td>
<td>280</td>
<td>+8%</td>
</tr>
<tr>
<td>3 月</td>
<td>350</td>
<td>+15%</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>合计</td>
<td>950</td>
<td>+11.7%</td>
</tr>
</tfoot>
</table>

各分区的作用

  • <caption>:为表格提供可见标题,屏幕阅读器会优先朗读它,是表格无障碍的重要组成。
  • <thead>:语义化地标识表头行,在打印长表格时浏览器会在每页重复显示表头。
  • <tbody>:包裹数据主体,当表格内容很长时可以独立滚动(配合 CSS)。
  • <tfoot>:放置汇总行。HTML 规范允许 <tfoot> 写在 <tbody> 之前,浏览器会自动渲染在底部。
小技巧

即使你不写 <tbody>,浏览器也会自动插入一个。你可以用开发者工具查看 DOM,会发现 <tr> 被包裹在自动生成的 <tbody> 里。

<th><td> 的区别

特性<th><td>
语义表头单元格数据单元格
默认样式粗体 + 居中正常字重 + 左对齐
scope 属性支持(标识作用范围)不支持
使用位置通常在 <thead> 中,也可在 <tbody> 中作为行标题<tbody><tfoot>

scope 属性

scope 用于明确 <th> 的作用范围,帮助屏幕阅读器正确关联表头与数据:

<table>
<thead>
<tr>
<th scope="col">科目</th>
<th scope="col">成绩</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">数学</th>
<td>95</td>
</tr>
<tr>
<th scope="row">英语</th>
<td>88</td>
</tr>
</tbody>
</table>
  • scope="col":表示该表头对整列生效
  • scope="row":表示该表头对整行生效
  • scope="colgroup" / scope="rowgroup":对列组/行组生效

单元格合并

表格支持通过 colspan(列合并)和 rowspan(行合并)来创建跨越多个单元格的复杂布局。

colspan —— 跨列合并

colspan 让一个单元格横跨多列:

<table border="1">
<tr>
<th colspan="3">学生成绩表</th>
</tr>
<tr>
<th>姓名</th>
<th>语文</th>
<th>数学</th>
</tr>
<tr>
<td>小明</td>
<td>90</td>
<td>95</td>
</tr>
</table>

合并效果示意:

┌──────────────────────────┐
│ 学生成绩表 │ ← colspan="3",横跨 3 列
├────────┬────────┬────────┤
│ 姓名 │ 语文 │ 数学 │
├────────┼────────┼────────┤
│ 小明 │ 90 │ 95 │
└────────┴────────┴────────┘

rowspan —— 跨行合并

rowspan 让一个单元格纵跨多行:

<table border="1">
<tr>
<th>类别</th>
<th>名称</th>
<th>价格</th>
</tr>
<tr>
<td rowspan="2">水果</td>
<td>苹果</td>
<td>5 元/斤</td>
</tr>
<tr>
<!-- 这里不需要再写"水果"的 <td>,因为上面已经 rowspan 占位 -->
<td>香蕉</td>
<td>3 元/斤</td>
</tr>
<tr>
<td rowspan="2">蔬菜</td>
<td>西红柿</td>
<td>4 元/斤</td>
</tr>
<tr>
<td>黄瓜</td>
<td>2 元/斤</td>
</tr>
</table>

合并效果示意:

┌────────┬────────┬────────┐
│ 类别 │ 名称 │ 价格 │
├────────┼────────┼────────┤
│ │ 苹果 │ 5元/斤 │
│ 水果 ├────────┼────────┤ ← rowspan="2"
│ │ 香蕉 │ 3元/斤 │
├────────┼────────┼────────┤
│ │ 西红柿 │ 4元/斤 │
│ 蔬菜 ├────────┼────────┤ ← rowspan="2"
│ │ 黄瓜 │ 2元/斤 │
└────────┴────────┴────────┘

混合使用 colspan 和 rowspan

<table border="1">
<tr>
<th colspan="2" rowspan="2">课程安排</th>
<th colspan="2">上午</th>
<th colspan="2">下午</th>
</tr>
<tr>
<th>第一节</th>
<th>第二节</th>
<th>第三节</th>
<th>第四节</th>
</tr>
<tr>
<td rowspan="2">周一</td>
<td></td>
<td>语文</td>
<td>数学</td>
<td>英语</td>
<td>体育</td>
</tr>
<tr>
<td></td>
<td>物理</td>
<td>化学</td>
<td>音乐</td>
<td>自习</td>
</tr>
</table>
合并时的常见错误
  1. 多写了单元格:被合并掉的位置不应再写 <td><th>,否则会导致表格变形。
  2. 数量不匹配:每行的"有效列数"(包括被 colspan/rowspan 占据的)必须一致。
  3. 调试技巧:先在纸上画出表格网格,标注每个单元格的位置,再编写 HTML。

列分组 <colgroup><col>

<colgroup><col> 用于对列进行分组和统一设置样式,避免为每个单元格重复添加样式:

<table>
<colgroup>
<col />
<col style="background-color: #f0f8ff;" />
<col style="background-color: #fff0f5;" />
</colgroup>
<thead>
<tr>
<th>姓名</th>
<th>语文</th>
<th>数学</th>
</tr>
</thead>
<tbody>
<tr>
<td>小明</td>
<td>90</td>
<td>95</td>
</tr>
<tr>
<td>小红</td>
<td>88</td>
<td>92</td>
</tr>
</tbody>
</table>

<col>span 属性可以让一个 <col> 元素覆盖多列:

<colgroup>
<col /> <!-- 第 1 列 -->
<col span="2" class="score-cols" /> <!-- 第 2~3 列共享样式 -->
</colgroup>
适用场景

<colgroup> 适合对整列设置背景色、宽度等样式。但列样式的优先级较低,单元格本身的样式会覆盖它。

表格的无障碍(Accessibility)

无障碍是表格开发中容易被忽视但非常重要的方面。

基本原则

  1. 始终提供 <caption>:让用户在"进入"表格之前了解表格的内容。
  2. 使用 <th> 搭配 scope:明确表头与数据的关联关系。
  3. 复杂表格使用 headers + id:当表头结构复杂(多级表头)时,用 idheaders 属性手动关联。

复杂表头的 headers 关联

<table>
<caption>各班各科平均成绩</caption>
<thead>
<tr>
<th id="class" rowspan="2">班级</th>
<th id="lang" colspan="2">语文</th>
<th id="math" colspan="2">数学</th>
</tr>
<tr>
<th id="lang-avg" headers="lang">平均分</th>
<th id="lang-pass" headers="lang">及格率</th>
<th id="math-avg" headers="math">平均分</th>
<th id="math-pass" headers="math">及格率</th>
</tr>
</thead>
<tbody>
<tr>
<th id="c1" headers="class">一班</th>
<td headers="c1 lang lang-avg">85</td>
<td headers="c1 lang lang-pass">92%</td>
<td headers="c1 math math-avg">90</td>
<td headers="c1 math math-pass">95%</td>
</tr>
</tbody>
</table>
判断是否需要 headers

简单表格(只有一行表头)使用 scope 即可。当表格存在多级表头合并单元格的表头时,才需要 headers + id 做精确关联。

表格常用 CSS 样式

边框与间距

/* 合并边框(去除双线) */
table {
border-collapse: collapse; /* 关键属性 */
}

/* 分离边框(保留间距) */
table {
border-collapse: separate;
border-spacing: 8px 4px; /* 水平间距 垂直间距 */
}

th, td {
border: 1px solid #ccc;
padding: 8px 12px;
}

border-collapse 是表格样式中最重要的属性:

border-collapse: separate(默认)    border-collapse: collapse
┌───┐ ┌───┐ ┌───┐ ┌───┬───┬───┐
│ A │ │ B │ │ C │ │ A │ B │ C │
└───┘ ┌───┐ ┌───┐ ├───┼───┼───┤
│ D │ │ E │ │ D │ E │ F │
└───┘ └───┘ └───┴───┴───┘
↑ 每个单元格独立边框 ↑ 相邻边框合并为一条线

斑马纹效果

/* 偶数行添加背景色 */
tbody tr:nth-child(even) {
background-color: #f9f9f9;
}

/* 悬停高亮 */
tbody tr:hover {
background-color: #e8f4fd;
}

固定表头(可滚动表体)

.table-wrapper {
max-height: 400px;
overflow-y: auto;
}

.table-wrapper thead th {
position: sticky;
top: 0;
background-color: #fff;
z-index: 1;
}

响应式表格

在小屏幕上,宽表格容易溢出。常见的处理方式:

/* 方式一:水平滚动 */
.table-container {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}

/* 方式二:让表格自适应宽度 */
table {
width: 100%;
table-layout: fixed; /* 列宽平均分配 */
}

td {
word-wrap: break-word;
overflow-wrap: break-word;
}

table-layout 属性

table-layout 控制浏览器如何计算列宽:

行为特点
auto(默认)根据所有单元格内容计算列宽灵活但渲染慢,需读取全部内容
fixed只根据第一行<col> 的宽度计算渲染快,适合大数据量表格
/* 大数据表格推荐 */
table {
table-layout: fixed;
width: 100%;
}
性能提示

当表格行数很多时(数百行以上),使用 table-layout: fixed 可以显著提升渲染性能,因为浏览器不需要遍历所有行来确定列宽。

表格布局 vs CSS 布局

早期的网页大量使用表格进行页面布局,这种做法如今已被淘汰。

何时使用表格

应该使用表格的场景:

  • 数据报表、统计表
  • 价格对比表
  • 课程表、日程表
  • 表单中的表格式布局(如设置页面的键值对展示)

不应该使用表格的场景:

  • 页面整体布局(头部、侧栏、内容区)
  • 导航菜单
  • 图片画廊
  • 卡片列表
为什么不用表格布局?
  1. 语义错误:表格是"数据"标签,用于布局会误导屏幕阅读器。
  2. 灵活性差:表格布局难以实现响应式设计。
  3. 代码冗余:需要大量嵌套标签,维护成本高。
  4. 渲染性能:表格需要全部内容加载后才能计算布局。

实战示例:完整的数据表格

下面是一个综合运用上述知识的完整示例:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>员工信息表</title>
<style>
.table-container {
overflow-x: auto;
}
table {
border-collapse: collapse;
width: 100%;
font-family: -apple-system, sans-serif;
}
caption {
font-size: 1.2em;
font-weight: bold;
padding: 12px;
caption-side: top; /* 标题在上方 */
text-align: left;
}
th, td {
border: 1px solid #ddd;
padding: 10px 14px;
text-align: left;
}
thead th {
background-color: #4a90d9;
color: #fff;
position: sticky;
top: 0;
}
tbody tr:nth-child(even) {
background-color: #f5f7fa;
}
tbody tr:hover {
background-color: #e8f0fe;
}
tfoot td {
font-weight: bold;
background-color: #f0f0f0;
}
</style>
</head>
<body>
<div class="table-container">
<table>
<caption>技术部员工信息表</caption>
<colgroup>
<col style="width: 60px;" />
<col style="width: 100px;" />
<col />
<col />
<col style="width: 120px;" />
</colgroup>
<thead>
<tr>
<th scope="col">工号</th>
<th scope="col">姓名</th>
<th scope="col">职位</th>
<th scope="col">部门</th>
<th scope="col">入职日期</th>
</tr>
</thead>
<tbody>
<tr>
<td>001</td>
<td>张三</td>
<td>前端工程师</td>
<td>技术部</td>
<td>2022-03-15</td>
</tr>
<tr>
<td>002</td>
<td>李四</td>
<td>后端工程师</td>
<td>技术部</td>
<td>2021-07-20</td>
</tr>
<tr>
<td>003</td>
<td>王五</td>
<td>测试工程师</td>
<td>技术部</td>
<td>2023-01-10</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="5">共 3 名员工</td>
</tr>
</tfoot>
</table>
</div>
</body>
</html>

面试高频问答

Q1:<th><td> 有什么区别?

:两者都是表格单元格,区别在于语义和默认样式。<th> 表示表头单元格,默认粗体居中,支持 scope 属性来声明作用范围(列或行);<td> 表示数据单元格,默认正常字重左对齐。在无障碍方面,屏幕阅读器会利用 <th> 的信息来为用户朗读对应的列名或行名。

Q2:colspanrowspan 分别是什么?使用时需要注意什么?

colspan 让单元格横跨多列rowspan 让单元格纵跨多行。使用时需要注意:

  • 被合并的位置不要再写多余的 <td> / <th>
  • 每行的有效列数(包括被 span 占据的)必须保持一致,否则表格会错位。
  • 可以同时使用 colspanrowspan,但要仔细计算每个单元格的坐标。

Q3:border-collapse: collapseseparate 有什么区别?

  • separate(默认值):每个单元格有独立的边框,单元格之间有间距(可用 border-spacing 控制)。
  • collapse:相邻单元格的边框合并为一条线,没有间距。实际开发中几乎总是使用 collapse,因为它更美观且避免了双线问题。

Q4:为什么不推荐用表格做页面布局?

:四个原因:

  1. 语义不正确<table> 在 HTML 规范中用于展示数据,用于布局会误导辅助技术(如屏幕阅读器)。
  2. 响应式困难:表格的行列结构是固定的,难以在不同屏幕尺寸下灵活调整。
  3. 代码维护难:表格布局需要大量嵌套的 <tr> <td>,代码冗余可读性差。
  4. 性能问题:浏览器必须加载完整个表格内容后才能计算布局(而 CSS 布局可以渐进式渲染)。 现代开发应使用 Flexbox 或 CSS Grid 来做布局。

Q5:如何实现表格的固定表头效果?

:使用 CSS position: sticky 实现:

thead th {
position: sticky;
top: 0;
background-color: #fff; /* 必须设置背景色,否则滚动时内容会透出 */
z-index: 1;
}

外层容器需要设置 max-heightoverflow-y: auto 来触发滚动。注意:如果祖先元素有 overflow: hiddensticky 会失效。

Q6:table-layout: fixed 有什么作用?

table-layout: fixed 让浏览器只根据第一行(或 <col> 定义的宽度)来确定列宽,而不是遍历所有行的内容。优点是渲染性能更好(尤其是大数据量表格),缺点是内容可能溢出单元格(需要配合 overflowword-wrap 处理)。

Q7:如何提升表格的无障碍性?

:关键措施包括:

  1. 使用 <caption> 提供表格标题。
  2. 使用 <th> 标识表头,并添加 scope="col"scope="row" 声明作用方向。
  3. 对于复杂的多级表头表格,使用 idheaders 属性精确关联表头与数据单元格。
  4. 保持表格结构简单——如果一个表格需要非常复杂的合并,考虑拆分成多个简单表格。