跳到主要内容

HTML 表格

表格是用于展示二维数据的重要 HTML 元素。本章将详细介绍 HTML 表格的创建、结构化、样式美化以及无障碍访问最佳实践。

表格的本质

什么是表格?

HTML 表格是一种用于展示表格数据的结构,即由行和列交叉组成的二维数据表。表格的核心目的是让数据之间的关系清晰可见——行标题和列标题帮助用户理解每个数据单元格的含义。

表格适合展示:

  • 数据列表(如成绩单、销售报表、价格表)
  • 日程表和时间表
  • 日历
  • 比较表格(如产品对比)
  • 统计数据

为什么表格结构很重要?

正确使用表格结构不仅关乎视觉呈现,更关乎信息的可访问性:

  • 屏幕阅读器支持:盲人用户依赖屏幕阅读器导航表格,正确的结构让屏幕阅读器能够正确朗读表头和数据的关系
  • 搜索引擎理解:搜索引擎通过表格结构理解数据内容
  • 响应式适配:结构清晰的表格更容易在不同设备上适配
重要提醒

不要使用表格进行网页布局! 过去常用表格来创建页面布局,但这违反了现代网页设计的最佳实践。表格应该只用于展示表格数据,页面布局应该使用 CSS(Flexbox 或 Grid)。

基础表格结构

核心元素

一个完整的 HTML 表格由以下核心元素组成:

元素说明是否必需
<table>表格容器必需
<tr>表格行(Table Row)必需
<th>表头单元格(Table Header)推荐
<td>数据单元格(Table Data)必需
<caption>表格标题推荐

简单表格示例

<table>
<tr>
<th>姓名</th>
<th>年龄</th>
<th>城市</th>
</tr>
<tr>
<td>张三</td>
<td>25</td>
<td>北京</td>
</tr>
<tr>
<td>李四</td>
<td>30</td>
<td>上海</td>
</tr>
</table>

这段代码创建了一个简单的三行三列表格。第一行是表头(<th>),后面两行是数据(<td>)。

元素的区别

<th><td> 有本质区别:

  • <th>(表头单元格):表示行或列的标题,浏览器默认会加粗居中显示
  • <td>(数据单元格):表示具体的数据内容,浏览器默认左对齐

这个区别不仅是视觉上的,更是语义上的。屏幕阅读器会告诉用户当前单元格是"表头"还是"数据",这对于理解表格内容至关重要。

表格结构分组

为了更好地组织表格结构,HTML 提供了三个结构分组元素:<thead><tbody><tfoot>

为什么需要结构分组?

使用结构分组的好处:

  1. 语义清晰:明确区分表头、表体和表脚
  2. 样式控制:可以为不同区域应用不同的样式
  3. 打印优化:长表格在打印时,表头和表脚可以在每页重复
  4. 滚动支持:在固定高度的表格中,可以只滚动表体

thead、tbody、tfoot 的使用

<table>
<caption>员工薪资表</caption>

<!-- 表头区域 -->
<thead>
<tr>
<th>姓名</th>
<th>部门</th>
<th>薪资</th>
</tr>
</thead>

<!-- 表体区域 -->
<tbody>
<tr>
<td>张三</td>
<td>技术部</td>
<td>15000</td>
</tr>
<tr>
<td>李四</td>
<td>市场部</td>
<td>12000</td>
</tr>
<tr>
<td>王五</td>
<td>财务部</td>
<td>13000</td>
</tr>
</tbody>

<!-- 表脚区域 -->
<tfoot>
<tr>
<td colspan="2">平均薪资</td>
<td>13333</td>
</tr>
</tfoot>
</table>

各部分说明:

元素作用特点
<thead>表头区域包含列标题,每张表格只能有一个
<tbody>表体区域包含主要数据,可以有多
<tfoot>表脚区域包含汇总或脚注信息
书写顺序

虽然 <tfoot> 在视觉上显示在表格底部,但在 HTML 中可以写在 <tbody> 之前。浏览器会自动调整显示顺序。不过,现代规范推荐按照 theadtbodytfoot 的顺序编写。

表格标题 caption

<caption> 元素为表格提供标题,应该紧跟在 <table> 标签之后。

基本用法

<table>
<caption>2024年第一季度销售报表</caption>
<tr>
<th>月份</th>
<th>销售额</th>
</tr>
<tr>
<td>一月</td>
<td>¥120,000</td>
</tr>
</table>

标题位置控制

通过 CSS 的 caption-side 属性可以控制标题位置:

caption {
caption-side: top; /* 默认,标题在表格上方 */
caption-side: bottom; /* 标题在表格下方 */
}

为什么标题很重要?

  • 快速识别:用户可以快速判断表格是否与他们的需求相关
  • 屏幕阅读器:屏幕阅读器会首先朗读标题,帮助盲人用户理解表格内容
  • SEO 优化:搜索引擎可以通过标题更好地理解表格内容

列分组 colgroup 和 col

<colgroup><col> 元素用于为表格的列定义属性,这对于批量设置列样式非常有用。

基本用法

<table>
<!-- 定义列样式 -->
<colgroup>
<col style="background-color: #f0f0f0; width: 150px;">
<col style="background-color: #e8e8e8; width: 100px;">
<col style="background-color: #f0f0f0; width: 100px;">
</colgroup>

<tr>
<th>产品名称</th>
<th>价格</th>
<th>库存</th>
</tr>
<tr>
<td>商品A</td>
<td>¥99</td>
<td>100</td>
</tr>
</table>

span 属性

使用 span 属性可以让一个 <col> 元素跨越多列:

<table>
<colgroup>
<col style="background-color: #f0f0f0;">
<col span="2" style="background-color: #e8e8e8;">
<col style="background-color: #f0f0f0;">
</colgroup>
<!-- 表格内容 -->
</table>

colgroup 的优势

  • 统一列宽:无需为每个单元格设置宽度
  • 批量样式:一次设置整列的背景色等样式
  • 代码简洁:减少重复的类名或内联样式

合并单元格

跨列合并 colspan

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

<table>
<tr>
<th colspan="3">上半年销售统计</th>
</tr>
<tr>
<th>一月</th>
<th>二月</th>
<th>三月</th>
</tr>
<tr>
<td>100</td>
<td>120</td>
<td>150</td>
</tr>
</table>

跨行合并 rowspan

rowspan 属性让一个单元格竖跨多行:

<table>
<tr>
<th rowspan="3">技术部</th>
<td>张三</td>
<td>工程师</td>
</tr>
<tr>
<td>李四</td>
<td>设计师</td>
</tr>
<tr>
<td>王五</td>
<td>测试员</td>
</tr>
</table>

同时使用 colspan 和 rowspan

<table>
<tr>
<th rowspan="2" colspan="2">产品/季度</th>
<th colspan="2">2023年</th>
</tr>
<tr>
<th>上半年</th>
<th>下半年</th>
</tr>
<tr>
<th rowspan="2">产品A</th>
<th>国内</th>
<td>100</td>
<td>120</td>
</tr>
<tr>
<th>海外</th>
<td>50</td>
<td>60</td>
</tr>
</table>

合并单元格的注意事项

  1. 合并后要减少单元格数量:如果一行有 4 列,某单元格 colspan="2",则该行只需要 3 个单元格
  2. 注意逻辑清晰:过度复杂的合并会让表格难以理解,也影响无障碍访问
  3. 屏幕阅读器会朗读合并范围:例如"产品季度,横跨 2 列,纵跨 2 行"

无障碍访问

表格的无障碍访问是确保所有用户都能理解表格内容的关键。以下是无障碍表格的核心要点。

使用 scope 属性

scope 属性告诉屏幕阅读器表头单元格是列标题还是行标题:

<table>
<tr>
<!-- 列标题:告诉屏幕阅读器这是列的标题 -->
<th scope="col">姓名</th>
<th scope="col">年龄</th>
<th scope="col">城市</th>
</tr>
<tr>
<!-- 行标题:告诉屏幕阅读器这是行的标题 -->
<th scope="row">张三</th>
<td>25</td>
<td>北京</td>
</tr>
<tr>
<th scope="row">李四</th>
<td>30</td>
<td>上海</td>
</tr>
</table>

scope 属性值:

说明使用场景
col列标题用于 <thead> 中的 <th>
row行标题用于每行第一个 <th>
colgroup列组标题用于跨多列的标题
rowgroup行组标题用于跨多行的标题

复杂表格的 headers 属性

对于复杂的表格(特别是使用了 colspanrowspan 的表格),使用 idheaders 属性明确建立单元格与表头的关联:

<table>
<thead>
<tr>
<th id="name">姓名</th>
<th id="project">项目</th>
<th id="q1">第一季度</th>
<th id="q2">第二季度</th>
</tr>
</thead>
<tbody>
<tr>
<th id="zhang" headers="name">张三</th>
<td headers="zhang project">项目A</td>
<td headers="zhang q1">100</td>
<td headers="zhang q2">120</td>
</tr>
<tr>
<th id="li" headers="name">李四</th>
<td headers="li project">项目B</td>
<td headers="li q1">80</td>
<td headers="li q2">90</td>
</tr>
</tbody>
</table>

headers 属性的值是一个或多个表头单元格的 id,用空格分隔。这样屏幕阅读器就能准确朗读:"张三,项目,第一季度,100"。

无障碍最佳实践

1. 始终使用正确的表头

<!-- 正确:使用 th 标记表头 -->
<tr>
<th scope="col">产品</th>
<th scope="col">价格</th>
</tr>

<!-- 错误:用 td 作为表头 -->
<tr>
<td style="font-weight: bold;">产品</td>
<td style="font-weight: bold;">价格</td>
</tr>

2. 为表格添加标题

<table>
<caption>员工薪资统计表</caption>
<!-- 表格内容 -->
</table>

3. 避免过于复杂的表格

如果表格过于复杂,考虑:

  • 拆分为多个简单表格
  • 使用替代的展示方式(如列表、卡片)

4. 不要用表格做布局

表格仅用于表格数据,不要用于页面布局。布局表格会:

  • 困扰屏幕阅读器用户
  • 在移动设备上难以适配
  • 违反 HTML 语义规范

表格样式美化

基础样式

/* 表格基础样式 */
table {
border-collapse: collapse; /* 合并相邻边框 */
width: 100%;
font-family: Arial, sans-serif;
}

/* 单元格样式 */
th, td {
border: 1px solid #ddd;
padding: 12px;
text-align: left;
}

/* 表头样式 */
th {
background-color: #3498db;
color: white;
font-weight: bold;
}

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

说明
collapse相邻单元格共享边框(推荐)
separate每个单元格有独立边框(默认)

斑马纹表格

交替行颜色提高可读性:

tbody tr:nth-child(even) {
background-color: #f9f9f9;
}

/* 或者使用 odd 选择奇数行 */
tbody tr:nth-child(odd) {
background-color: #ffffff;
}

悬停效果

高亮当前行:

tbody tr:hover {
background-color: #e8f4fc;
}

/* 或者只高亮单元格 */
td:hover {
background-color: #f0f7ff;
}

响应式表格

在小屏幕上让表格可滚动:

<div class="table-container">
<table>
<!-- 表格内容 -->
</table>
</div>
.table-container {
overflow-x: auto;
-webkit-overflow-scrolling: touch; /* iOS 平滑滚动 */
}

/* 或者让表格本身可滚动 */
table {
display: block;
overflow-x: auto;
white-space: nowrap;
}

固定表头滚动

长表格固定表头:

thead {
position: sticky;
top: 0;
background-color: white;
z-index: 1;
}

/* 确保表头有背景色,否则会透明 */
th {
background-color: #f5f5f5;
border-bottom: 2px solid #ddd;
}

固定列滚动

固定左侧列:

/* 固定第一列 */
th:first-child,
td:first-child {
position: sticky;
left: 0;
background-color: white;
z-index: 1;
}

/* 固定前两列 */
th:nth-child(-n+2),
td:nth-child(-n+2) {
position: sticky;
left: 0;
background-color: white;
}

分隔线样式

只在行之间显示分隔线:

table {
border-collapse: collapse;
}

th, td {
border: none;
padding: 12px;
border-bottom: 1px solid #eee;
}

/* 移除最后一行的边框 */
tr:last-child td {
border-bottom: none;
}

卡片式表格

现代卡片风格:

table {
border-collapse: separate;
border-spacing: 0 10px; /* 行间距 */
}

tr {
background: white;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
border-radius: 8px;
}

th, td {
border: none;
padding: 16px;
}

td:first-child {
border-radius: 8px 0 0 8px;
}

td:last-child {
border-radius: 0 8px 8px 0;
}

完整示例

示例1:员工信息表

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>员工信息表</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
line-height: 1.6;
padding: 20px;
background-color: #f5f5f5;
}

.table-container {
overflow-x: auto;
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

table {
width: 100%;
border-collapse: collapse;
}

caption {
padding: 16px;
font-size: 18px;
font-weight: bold;
text-align: left;
background-color: #f8f9fa;
border-bottom: 1px solid #eee;
}

th, td {
padding: 12px 16px;
text-align: left;
border-bottom: 1px solid #eee;
}

th {
background-color: #3498db;
color: white;
font-weight: 600;
position: sticky;
top: 0;
}

tbody tr:hover {
background-color: #f8f9fa;
}

tbody tr:nth-child(even) {
background-color: #fafafa;
}

tbody tr:nth-child(even):hover {
background-color: #f0f7ff;
}

tfoot {
background-color: #f8f9fa;
font-weight: bold;
}

tfoot td {
border-bottom: none;
}

/* 状态标签样式 */
.status {
display: inline-block;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
}

.status-active {
background-color: #d4edda;
color: #155724;
}

.status-inactive {
background-color: #f8d7da;
color: #721c24;
}
</style>
</head>
<body>
<div class="table-container">
<table>
<caption>员工信息表 - 2024年第一季度</caption>
<thead>
<tr>
<th scope="col">工号</th>
<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>EMP001</td>
<td>张三</td>
<td>技术部</td>
<td>高级工程师</td>
<td><time datetime="2020-03-15">2020年3月15日</time></td>
<td><span class="status status-active">在职</span></td>
</tr>
<tr>
<td>EMP002</td>
<td>李四</td>
<td>市场部</td>
<td>市场经理</td>
<td><time datetime="2019-07-01">2019年7月1日</time></td>
<td><span class="status status-active">在职</span></td>
</tr>
<tr>
<td>EMP003</td>
<td>王五</td>
<td>财务部</td>
<td>财务主管</td>
<td><time datetime="2021-01-10">2021年1月10日</time></td>
<td><span class="status status-inactive">离职</span></td>
</tr>
<tr>
<td>EMP004</td>
<td>赵六</td>
<td>技术部</td>
<td>前端工程师</td>
<td><time datetime="2022-05-20">2022年5月20日</time></td>
<td><span class="status status-active">在职</span></td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="3">统计:共 4 人</td>
<td colspan="2">在职:3 人</td>
<td>离职:1 人</td>
</tr>
</tfoot>
</table>
</div>
</body>
</html>

示例2:课程表

<table>
<caption>2024年春季学期课程表</caption>
<thead>
<tr>
<th scope="col">时间</th>
<th scope="col">周一</th>
<th scope="col">周二</th>
<th scope="col">周三</th>
<th scope="col">周四</th>
<th scope="col">周五</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">8:00-10:00</th>
<td>高等数学</td>
<td>大学英语</td>
<td>高等数学</td>
<td>大学英语</td>
<td>体育</td>
</tr>
<tr>
<th scope="row">10:00-12:00</th>
<td>计算机基础</td>
<td>线性代数</td>
<td>计算机基础</td>
<td>线性代数</td>
<td rowspan="2">选修课</td>
</tr>
<tr>
<th scope="row">14:00-16:00</th>
<td>程序设计</td>
<td>物理学</td>
<td>程序设计</td>
<td>物理学</td>
</tr>
<tr>
<th scope="row">16:00-18:00</th>
<td colspan="4">自习</td>
<td>班会</td>
</tr>
</tbody>
</table>

示例3:复杂的多级表头表格

<table>
<caption>各地区季度销售统计</caption>
<thead>
<tr>
<th rowspan="2" scope="col">地区</th>
<th colspan="2" scope="colgroup">上半年</th>
<th colspan="2" scope="colgroup">下半年</th>
<th rowspan="2" scope="col">年度总计</th>
</tr>
<tr>
<th scope="col">Q1</th>
<th scope="col">Q2</th>
<th scope="col">Q3</th>
<th scope="col">Q4</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">华北</th>
<td>120</td>
<td>150</td>
<td>180</td>
<td>200</td>
<td>650</td>
</tr>
<tr>
<th scope="row">华东</th>
<td>200</td>
<td>220</td>
<td>250</td>
<td>280</td>
<td>950</td>
</tr>
<tr>
<th scope="row">华南</th>
<td>180</td>
<td>190</td>
<td>210</td>
<td>230</td>
<td>810</td>
</tr>
</tbody>
<tfoot>
<tr>
<th scope="row">合计</th>
<td>500</td>
<td>560</td>
<td>640</td>
<td>710</td>
<td>2410</td>
</tr>
</tfoot>
</table>

表格最佳实践总结

结构规范

规范说明
使用 <caption>为每个表格添加描述性标题
使用 <thead><tbody><tfoot>明确表格结构
使用 <th> 标记表头让屏幕阅读器识别标题
添加 scope 属性明确表头与数据的关系
复杂表格使用 headers明确单元格与表头的关联

样式建议

  1. 使用 border-collapse: collapse:统一边框样式
  2. 足够的内边距padding: 12px 左右,确保内容易读
  3. 交替行颜色:提高大数据量表格的可读性
  4. 悬停效果:帮助用户追踪当前行
  5. 响应式处理:小屏幕使用水平滚动

无障碍清单

  • 每个表格都有 <caption>
  • 表头使用 <th> 而非 <td>
  • 列标题设置 scope="col"
  • 行标题设置 scope="row"
  • 复杂表格使用 idheaders
  • 不使用表格做页面布局
  • 在移动端可正常访问(可滚动)

小结

本章学习了:

  1. 表格基础<table><tr><th><td> 元素
  2. 结构分组<thead><tbody><tfoot> 分区
  3. 表格标题<caption> 元素的使用
  4. 列分组<colgroup><col> 的应用
  5. 合并单元格colspanrowspan 属性
  6. 无障碍访问scopeheaders 属性
  7. 样式美化:边框、斑马纹、悬停效果、响应式处理
  8. 最佳实践:结构规范、样式建议、无障碍清单

练习

  1. 创建一个包含标题、表头、表体和表脚的完整表格
  2. 使用 scope 属性为一个双向表头的表格添加无障碍支持
  3. 实现一个带斑马纹和悬停效果的样式表格
  4. 创建一个使用 colspanrowspan 的复杂表格
  5. 实现一个响应式表格,在小屏幕上可以水平滚动
  6. 创建一个课程表,包含多级表头