在网页数据展示中,表格是最常用的结构化载体。当我们需要表达带有层级或分组含义的表头时,单纯的一行一列往往不够用,这时就要用到 HTML 提供的跨行能力。跨行表头单元格指的是一个表头单元格在垂直方向上同时占据多行,用来统驭其下方若干行的内容,这种需求在统计报表、课程表、财务明细中非常普遍。

一、rowSpan 属性的基本定义
rowSpan 是 HTML 表格中 <td> 与 <th> 标签支持的一个属性,用来指定单元格应该横跨的行数。它的取值是一个非负整数,默认值为 1。当某个单元格设置了 rowSpan="3",就意味着这个单元格在视觉和语义上会覆盖当前行及下方两行同一列的位置。
需要注意的是,rowSpan 只影响单元格在行方向上的延伸,并不会改变列的位置。被延伸覆盖的区域,在后续行的 HTML 代码中不能再出现对应列的单元格,否则浏览器会强行插入空白或导致布局错乱。理解这一点是写出正确跨行表头的前提。
1.1 最简单的跨行写法
下面是一段最基础的跨行表头代码,第一列的表头纵向合并了两行:
<table border="1">
<tr>
<th rowSpan="2">项目</th>
<th>第一季度</th>
<th>第二季度</th>
</tr>
<tr>
<th>收入</th>
<th>支出</th>
</tr>
<tr>
<td>A组</td>
<td>100</td>
<td>80</td>
</tr>
</table>
在上面的结构中,项目这一 <th> 通过 rowSpan="2" 占据了第一行和第二行的第一列,因此第二行就不再需要写第一列的单元格,直接从第二列开始写两个子表头。浏览器会自然地把项目放在左侧跨两行的位置。
二、多行多列混合的表头设计
实际业务中,表头常常既需要跨行也需要跨列。虽然本文聚焦跨行,但跨行往往与横向的 colSpan 配合使用。设计这类表头时,推荐先在草稿上画出网格,标出每个单元格的起止行号和列号,再转换为 HTML。
一个容易犯错的地方是:开发者习惯按视觉顺序写代码,却忽略了 HTML 表格的解析规则——表格是按行顺序、每行按列顺序依次排布的。如果上一行某列被 rowSpan 占用,下一行该列就自动被跳过,不需要、也不能再写单元格。
2.1 嵌套分组的表头示例
假设我们要做一个员工考核表,左侧大表头为部门,跨两行;右侧第一行是考核维度,第二行是该维度下的具体指标:
<table border="1">
<tr>
<th rowSpan="2">部门</th>
<th colSpan="2">业绩指标</th>
<th rowSpan="2">综合评级</th>
</tr>
<tr>
<th>销售额</th>
<th>回款率</th>
</tr>
<tr>
<td>研发部</td>
<td>320万</td>
<td>95%</td>
<td>A</td>
</tr>
<tr>
<td>市场部</td>
<td>410万</td>
<td>88%</td>
<td>B</td>
</tr>
</table>
这段代码里,部门与综合评级都使用了 rowSpan="2",因此它们分别固定在最左和最右并贯穿前两行。中间部分第一行用 colSpan="2" 合并了业绩指标,第二行再拆成销售额与回款率。整体结构清晰,没有冗余单元格。
2.2 常见错位问题排查
如果发现表格在浏览器中突然多出一列或者某行少内容,多半是 rowSpan 计数错误。比如把 rowSpan 写成 3,但实际只跨了两行数据,那么第三行对应位置就会空出一格,导致后续列整体右移。此时应核对每一行实际生成的 <td> 或 <th> 数量是否等于总列数减去被上方跨行占用的列数。
另一个隐蔽问题是闭合标签遗漏。由于跨行后某些行少写了单元格,若再少写 </tr>,浏览器容错机制可能自动补全,但渲染结果往往偏离预期。因此写复杂表头时,建议每写完一行就数一下列数。
三、使用 CSS 与可访问性补充
虽然 rowSpan 是纯 HTML 属性,但在现代开发中,我们通常用 CSS 控制边框与对齐,让跨行表头更美观。同时,对于使用屏幕阅读器的用户,合理的表头结构至关重要。<th> 本身带有 scope 属性,跨行表头可设置 scope="rowgroup" 来声明它统驭一个行组。
举例来说,如果部门是跨行表头,写成 <th rowSpan="2" scope="rowgroup">部门</th>,辅助技术就能明白其下方两行数据都从属于该部门。这不会更改视觉表现,但显著提升了语义正确性。
3.1 结合 scope 的完整示例
<table border="1">
<tr>
<th rowSpan="2" scope="rowgroup">部门</th>
<th colSpan="2">业绩指标</th>
</tr>
<tr>
<th>销售额</th>
<th>回款率</th>
</tr>
<tr>
<td>运营部</td>
<td>280万</td>
<td>91%</td>
</tr>
</table>
通过 scope 的配合使用,跨行表头不只是看上去合并了单元格,而是在文档语义层面真正成为了行组的标题。这对于企业后台、数据平台等注重可访问性的项目尤其重要。
四、总结与实践建议
实现跨行表头的核心就是正确使用 rowSpan 属性,并严格遵循表格按行、按列顺序渲染的规则。动手前先画网格图,标明每个表头的跨越范围,可以大幅降低出错概率。在代码层面,优先使用 <th> 而非 <td> 作为表头,并辅以 scope 等语义属性。
当表格复杂度继续上升,比如需要动态生成跨行结构时,可以在 JavaScript 中维护一个二维模型,计算每行应跳过的列索引,再拼装 HTML 字符串。但无论怎样封装,底层依然离不开本文介绍的 rowSpan 计数逻辑。掌握它,就能轻松应对绝大多数原生 HTML 表格布局场景。