colspan="N" 是删除右侧N−1个单元格,rowspan="N" 是删除下方N−1行对应列的单元格;二者均非样式设置,而是强制移除DOM节点以保证表格结构对齐。

colspan="N" 不是“加宽”,而是“删掉右边 N−1 个 <td>”
<p>写 <code>colspan="3" 的那一刻,你就承诺:这一行里,从当前 <td> 开始,它要占据 3 列位置,所以它右边本该存在的 2 个 <code><td> 必须彻底删除。浏览器不会帮你跳过,它只按你写的标签数一列一列排。
<ul>
<li>错误写法:<code><tr>
<td colspan="3">A</td>
<td>B</td>
<td>C</td>
<td>D</td>
</tr> → 这行实际有 5 列(3+1+1),但表头若只有 4 列,下一行就会错位
<tr>
<td colspan="3">A</td>
<td>D</td>
</tr> → 总列数 = 3 + 1 = 4,与其他行对齐Array.from(tr.children).reduce((s, el) => s + (el.colSpan || 1), 0) 算每行真实列数,所有 <tr> 结果必须一致
<h3>rowspan="N" 是“删除指令”,不是“样式设置”</h3>
<p><code>rowspan 的本质是告诉浏览器:“从这一行起,下面 N−1 行的同一列位置,别再渲染任何 <td> 或 <code><th>”。它不接受留空、不接受 <code>v-if 显隐、不接受 <td></td> 占位——必须整行整列地删掉那个 <td> 标签。
<ul><li>典型翻车:<code><tr>
<td rowspan="2">Name</td>
<td>Alice</td>
</tr>
<tr>
<td>Name</td>
<td>Bob</td>
</tr> → 第二行第一个 <td> 必须消失,否则表格裂开
<li>跨 <code><thead>/<code><tbody> 时更危险:<code><thead> 里 <code><th rowspan="2">,那 <code><tbody> 第一行对应列也得空着,不能出现 <code><td>
<li>动态渲染(如 Vue/React)中,不能靠条件渲染控制显隐;得提前聚合数据,跳过该列的整个 <code><td> 渲染逻辑
<h3>同时用 <code>colspan 和 rowspan 时,二维覆盖区域必须手动清空
一个 <td rowspan="3" colspan="2"> 覆盖的是一个 3 行 × 2 列的矩形区域。它不只是“变大”,而是“占位+删格”:所在行右侧删 1 个 <code><td>,下方连续 2 行对应列位置各删 1 个 <code><td>(共 2 个),总计删掉 3 个单元格。
<ul>
<li>先画草图标定被覆盖的所有坐标(比如第 1 行第 1–2 列、第 2 行第 1–2 列、第 3 行第 1–2 列),再逐行检查是否漏删</li>
<li>常见陷阱:多级表头中 <code><thead> 某行用了 <code>colspan,但 <tbody> 行数或列数没同步校验,导致 <code>border-collapse: collapse 失效、边框断开
colspan="0" 或 rowspan="0" 在 HTML5 中被忽略,不报错但无效调试时最有效的三件事
别猜,直接看结构和行为:
立即学习“前端免费学习笔记(深入)”;
- 打开 Chrome DevTools → Elements 面板 → 右键表格 → “Edit as HTML”,临时删掉疑似多余的
<td>,看是否恢复对齐 <li>选中合并单元格 → 查看 box model,确认是否真跨行/跨列渲染;再点开它下方/右侧的 <code><tr>,检查对应列是否真的没有节点 <li>运行 <code>document.querySelector('table').rows[0].cells.length和document.querySelector('tbody tr:first-child').cells.length,对比是否相等
复杂表头往往错在“删得不够干净”,而不是属性写错了。每次加 rowspan 或 colspan,都要问自己:我删掉了哪些 <td>?它们在 DOM 里真的不存在吗?</td>



















