colspan="n" 表示合并当前单元格及其后n−1个单元格,而非额外添加列;rowspan="n" 表示当前单元格向下占据n行,对应下方行同列位置必须留空;二者混用需严格校验行列总数并手动清理被合并区域的冗余标签。

colspan="n" 不是“加宽”,而是删掉后面 n−1 个 <td>
<p>很多人写 <code>colspan="3" 后,还在同一行里继续写三个 <td>,结果整行变成 4 列(3+1),下一行对不齐。浏览器不会帮你跳过,它只按你写的 <code><td> 数量逐列排布。
<ul><li>若某行总列数应为 5,你在第一个 <code><td> 上写 <code>colspan="3",那这一行后续只能再写 2 个 <td>(不是 4 个)
<li>验证方法:用 <code>Array.from(tr.children).reduce((s, el) => s + (el.colSpan || 1), 0) 计算每行真实列数,确保所有 <tr> 结果一致
<li>常见错例:<code><tr>
<td colspan="3">A</td>
<td>B</td>
<td>C</td>
<td>D</td>
</tr> → 这行实际有 5 列,但第 4 个 <td> 会挤到第 5 列,导致下一行错位
<h3>rowspan="n" 要求下一行起,同列位置彻底留空</h3>
<p>写了 <code>rowspan="2" 却还在下一行对应列补了 <td>,表格立刻裂开。因为 <code>rowspan="2" 表示“本格向下吞掉正下方那一格”,所以下一行、同一列不能再出现任何 <td> 或 <code><th>。
<ul><li><code><thead> 和 <code><tbody> 是独立的行容器,但列坐标必须对齐:如果 <code><thead> 里用了 <code>rowspan,<tbody> 第一行对应列也必须跳过
<li>典型反例:<code><tr>
<td rowspan="2">Name</td>
<td>Alice</td>
</tr>
<tr>
<td>Name</td>
<td>Bob</td>
</tr> ❌ 错位;正确写法是第二行去掉第一个 <td>
<li>调试技巧:打开开发者工具,选中合并单元格,看 box model 是否跨行渲染;再检查 DOM 中下一行同列是否真的没节点</li>
<h3>同时用 <code>colspan 和 rowspan 时,行列总数必须双重校验
二维合并(比如一个单元格横跨 2 列、纵跨 3 行)会让结构陡然复杂,稍一疏忽整张表就偏移。这不是属性冲突,而是网格坐标被你手动改写了,但没同步清理周边。
- 先画草图标出每个单元格的起始行列、跨度,再编码——别靠脑子硬算
- 每一行的列总数要一致,每一列的行总数也要一致(尤其跨
<thead>/<code><tbody> 时) <li>如果合并区域右下角刚好卡在 <code><tbody> 开头,注意第一行 <code><tr> 里被 <code>rowspan占用的列,<tbody> 的首行不能补 <h3>嵌套表格或复杂多级表头,优先考虑语义与可访问性</h3> <p>用 <code>colspan/rowspan拼出来的“多维表头”,屏幕阅读器很难理解层级关系,scope属性也难准确绑定。真正需要表达多维度数据时,原生表格容易失焦。立即学习“前端免费学习笔记(深入)”;
- 如果只是视觉分组(比如“销售部”下辖“Q1/Q2/Q3”),用 CSS Grid + 语义化
<section></section>更可控、更易测试 - 若必须用表格,至少给每个
<th> 显式设置 <code>scope="col"或scope="row",并用aria-labelledby关联逻辑父级 - 嵌套表格(
<table> 套 <code><table>)会破坏表结构扁平性,导致键盘导航断裂、打印样式异常,且无法被大多数辅助技术解析 复杂点不在属性本身,而在你删掉了哪些标签、又漏掉了哪些留空。浏览器从不报错,它只是默默按你写的 DOM 渲染——错位、错行、错列,全是你没亲手“擦掉”的位置。</table>
- 如果只是视觉分组(比如“销售部”下辖“Q1/Q2/Q3”),用 CSS Grid + 语义化



















