合并单元格需严格遵循行列占位规则:colspan值之和必须等于总列数,rowspan则动态减少后续行可用列数,被覆盖单元格必须彻底删除,否则导致dom失真、js获取失败及无障碍访问异常。

合并表格单元格区域不是加属性就能跑通,rowspan 和 colspan 本质是定义一个矩形逻辑区域的起点,后续所有行的 它不是“加宽”,是“占位”: 这不是属性叠加,是在二维表格矩阵中划出一个矩形区域:<td> 数量必须动态减去已被占用的列数,否则结构就错了——浏览器不报错,但 JS 拿不到 <code>row.cells、打印错行、屏幕阅读器读串,全是静默崩溃。
colspan="n" 怎么算才不崩:每行总和必须等于总列数
colspan="3" 表示这个单元格向右连续占据 3 列位置。整行所有 <th> 和 <code><td> 的 <code>colspan 值加起来,必须严格等于该表格设计的总列数(通常由第一行表头列数决定)。
<th colspan="2">销量</th>
<th>Q1</th>
<th>Q2</th> ✅(2 + 1 + 1 = 4 列)<th colspan="2">销量</th>
<th>Q1</th>
<th>Q2</th>
<th>Q3</th> ❌(2 + 1 + 1 + 1 = 5),浏览器会忽略最后一个或补空 <td>,DOM 已失真
<li>
<code>colspan 值必须是正整数:colspan="2" 有效,colspan="2px" 或 colspan="two" 会被完全忽略rowspan="n" 不是拉高单元格,是“锁列”
rowspan 不改变当前行高度,而是从当前行起,“向下锁定该列位置共 n 行”。后续每行能写的 <td> 数量 = 总列数 − 当前列已被上层 <code>rowspan 占用的列数。
<td rowspan="3">服务器</td>
<td>CPU</td>,那下一行只能写 <td>内存</td>(第 1 列被锁,只剩第 2 列可填)<td>内存</td>
<td>硬盘</td>,JS 中 row.cells[0] 可能为 undefined,尤其在动态插入行后更明显<td> 数 = 总列数 − 已被上层 <code>rowspan 占用的列数
同时用 rowspan 和 colspan:被覆盖的单元格必须删干净
<th rowspan="2" colspan="3">标题</th> 覆盖了 2×3 = 6 个原始格子,其余 5 个位置在 HTML 中必须彻底不存在。
<td></td>,也不能用注释占位<td rowspan="2"> 占了第 1 列,则它右边的 <code><td colspan="3"> 必须从第 2 列开始算起,整行仍需凑够总列数(如总列数为 5,已占 1 列,剩下 4 列要靠 <code>colspan="3" + <td> = 3 + 1 实现)
<li>改完一个 <code>rowspan="2",就得立刻确认下面两行各自还剩几个 <td> 可写——这才是真正难维护的地方
<h3>复杂表头建议先手绘草图再编码</h3>
<p>直接写代码试错成本极高。真实项目中,最常卡住的位置不是不会写 <code>colspan="2",而是在改完第三处 rowspan 后,忘了顺手重算后面两行的单元格数量。草图上标出每个单元格覆盖的行列范围,转成 HTML 才不容易漏删、错位、多写。
响应式场景下,rowspan/colspan 在小屏不会折行或重组,只会横向溢出;真要兼容移动端,优先用 display: grid 或卡片堆叠,而不是硬撑表格结构。











