如何在 HTML 中制作表格
表格是一个由行(<tr>)组成的 <table> 元素,每一行又由单元格组成:表头单元格用 <th>,数据单元格用 <td>。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Table</title>
<style>
th, td { border: 1px solid #9ca3af; padding: 6px 10px; }
</style>
</head>
<body>
<table>
<tr>
<th>Name</th>
<th>Language</th>
<th>Year</th>
</tr>
<tr>
<td>Ada</td>
<td>Python</td>
<td>2023</td>
</tr>
<tr>
<td>Linus</td>
<td>C</td>
<td>2021</td>
</tr>
</table>
</body>
</html>
表格本身没有边框;那一条 CSS 规则给它加上了边框,好让你看清单元格。列是由单元格形成的:每一行的第一个单元格是第一列,第二个是第二列,以此类推。每一行的单元格数量应该相同。删掉一个 <td>,那一行就会提前结束,留下一个空缺。
<th> 单元格默认加粗并居中。但这只是外观;重要的是它们是标签,下一节就讲这个。
表格元素
| 元素 | 作用 |
|---|
<table> | 表格本身 |
<caption> | 表格的标题,是 <table> 的第一个子元素 |
<thead> | 表头行的分组 |
<tbody> | 主体行的分组(一个表格可以有多个) |
<tfoot> | 表尾行的分组,比如合计 |
<tr> | 一行 |
<th> | 一个表头单元格 |
<td> | 一个数据单元格 |
<colgroup>、<col> | 列,用于给整列设置样式(宽度、背景) |
边框的内容见 HTML 表格边框,跨多列或多行的单元格见 colspan 和 rowspan。
表头单元格、scope 与 caption
如果一个表格顶部和侧边都有表头,就需要说明每个表头指向哪个方向。scope="col" 标注下方的列,scope="row" 标注旁边的行。<caption> 给表格一个可见的标题:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Headers and caption</title>
<style>
table { border-collapse: collapse; font-family: system-ui, sans-serif; }
caption { font-weight: bold; padding-bottom: 6px; }
th, td { border: 1px solid #9ca3af; padding: 6px 10px; }
thead th { background: #e5e7eb; }
tbody th { text-align: start; }
</style>
</head>
<body>
<table>
<caption>Opening hours</caption>
<thead>
<tr>
<th scope="col">Day</th>
<th scope="col">Opens</th>
<th scope="col">Closes</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Monday</th>
<td>9:00</td>
<td>17:00</td>
</tr>
<tr>
<th scope="row">Saturday</th>
<td>10:00</td>
<td>14:00</td>
</tr>
</tbody>
</table>
</body>
</html>
有了这样的标记,屏幕阅读器移到 14:00 这个单元格时,就能读出“Saturday, Closes, 14:00”,数字永远不会被孤零零地读出来。对于只有一行表头的简单表格,即使没有 scope,浏览器通常也能推断出表头,但写上它不费任何事,还能免去猜测。
caption 也是屏幕阅读器到达表格时会读出的内容。比起放在表格上方、和表格没有关联的标题,更推荐使用它。
thead、tbody 与 tfoot
这三个分组把行分成表头、主体和表尾。表尾是放合计的地方:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>thead tbody tfoot</title>
<style>
table { border-collapse: collapse; font-family: system-ui, sans-serif; }
th, td { padding: 6px 12px; border-bottom: 1px solid #e5e7eb; }
thead th { border-bottom: 2px solid #111827; text-align: start; }
tfoot td, tfoot th { border-top: 2px solid #111827; font-weight: bold; }
.num { text-align: end; font-variant-numeric: tabular-nums; }
</style>
</head>
<body>
<table>
<thead>
<tr><th scope="col">Item</th><th scope="col" class="num">Price</th></tr>
</thead>
<tbody>
<tr><td>Notebook</td><td class="num">4.50</td></tr>
<tr><td>Pens (10)</td><td class="num">11.20</td></tr>
<tr><td>Backpack</td><td class="num">39.00</td></tr>
</tbody>
<tfoot>
<tr><th scope="row">Total</th><td class="num">54.70</td></tr>
</tfoot>
</table>
</body>
</html>
有两个细节让数字更易读:text-align: end 让小数点对齐(每个价格都有两位小数),font-variant-numeric: tabular-nums 让每个数字的宽度相同。
如果你像第一个例子那样直接在 <table> 中写 <tr>,浏览器仍然会在这些行外面加上一个 <tbody>。这就是为什么 table > tr 这样的 CSS 选择器永远匹配不到任何东西:在页面中,这些行位于 tbody 里面。请使用 table tr,或者自己写上 <tbody>。
表格样式:斑马纹、悬停与宽度
:nth-child 给交替的行加上条纹,:hover 高亮指针下方的行,width: 100% 让表格撑满它的容器:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Striped table</title>
<style>
table { width: 100%; border-collapse: collapse; font-family: system-ui, sans-serif; }
th, td { padding: 8px 12px; text-align: start; }
thead th { background: #2563eb; color: #fff; }
tbody tr:nth-child(even) { background: #f3f4f6; }
tbody tr:hover { background: #dbeafe; }
</style>
</head>
<body>
<table>
<thead>
<tr><th scope="col">Course</th><th scope="col">Lessons</th><th scope="col">Level</th></tr>
</thead>
<tbody>
<tr><td>HTML</td><td>40</td><td>Beginner</td></tr>
<tr><td>CSS</td><td>55</td><td>Beginner</td></tr>
<tr><td>JavaScript</td><td>120</td><td>Beginner</td></tr>
<tr><td>React</td><td>60</td><td>Intermediate</td></tr>
</tbody>
</table>
</body>
</html>
text-align: start 让表头文字和下方的数据对齐;否则 th 是居中的。用 start 而不是 left,在从右到左书写的语言中,对齐方式也能保持正确。
可滚动的响应式表格
表格无法缩小到比内容还窄。在手机上,一个很宽的表格会把整个页面往旁边推。所以应该把它包在一个可以滚动的容器里:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Scrolling table</title>
<style>
.scroll { max-width: 300px; overflow-x: auto; border: 1px solid #e5e7eb; }
table { border-collapse: collapse; white-space: nowrap; }
th, td { padding: 6px 12px; border-bottom: 1px solid #e5e7eb; text-align: start; }
</style>
</head>
<body>
<div class="scroll" tabindex="0" role="region" aria-label="Plans">
<table>
<thead>
<tr><th scope="col">Plan</th><th scope="col">Price</th><th scope="col">Courses</th><th scope="col">Certificates</th><th scope="col">Support</th></tr>
</thead>
<tbody>
<tr><td>Free</td><td>0</td><td>Some</td><td>No</td><td>Community</td></tr>
<tr><td>Pro</td><td>12 per month</td><td>All</td><td>Yes</td><td>Email</td></tr>
</tbody>
</table>
</div>
</body>
</html>
这里把容器的宽度限制在 300 像素,模拟手机屏幕;试着横向滚动它。tabindex="0" 让键盘用户可以聚焦到这个容器,并用方向键滚动它;role="region" 加上 aria-label 给这一站起了个名字。
表格用于展示数据
当内容真的有行和列时才使用表格:价格、时间表、对比、结果。不要用表格在页面上把东西并排摆放。布局表格会让屏幕阅读器困惑,因为它们会读出毫无意义的行和列,而且无法适应小屏幕。布局请交给 CSS 的 flexbox 和 grid。
常见错误
- 各行的单元格数量不同。短的行会提前结束,各列也就对不齐了。
- 用加粗文字的
<td> 作为表头。请使用 <th>,让表头成为真正的标签。
- 使用过时的属性
border、cellpadding、cellspacing、bgcolor 和 align。请改用 CSS。
- 把内容直接放在
<tr> 或 <table> 中,而不是放在单元格里。浏览器会把它移出表格,通常移到表格上方。
- 在表格上方放一个标题,而不是使用
<caption>。
- 在 CSS 中写
table > tr。浏览器已经插入了一个 <tbody>;请写 table tr 或 tbody tr。