tbody 必须显式书写,不可依赖浏览器自动补全,否则导致选择器失效、打印分页异常、屏幕阅读器语义断裂及固定表头错位等问题。

tbody 必须显式写,不能靠浏览器自动补全
浏览器确实会为没写 <tbody> 的表格自动创建一个隐式节点,但这个节点无法被 <code>document.querySelector('tbody') 稳定获取,CSS 选择器如 tbody tr:nth-child(odd) 也常失效。更严重的是,<thead> 和 <code><tfoot> 在无显式 <code><tbody> 时行为不可控——打印分页不重复表头、固定表头滚动错位、屏幕阅读器播报语义断裂。
<ul><li>所有含 <code><thead> 或 <code><tfoot> 的表格,<code><tbody> 是强制要求,不是“可选”
<li>服务端模板(EJS、Django、Thymeleaf)中,循环生成的 <code><tr> 必须包在 <code><tbody> 标签内,不能直接塞进 <code><table><li>React/Vue 中用条件渲染控制某组数据时,应包裹整个 <code><tbody> 节点,而非只控制内部 <code><tr>
<h3>多个 tbody 的真实用途和常见误用</h3>
<p>多个 <code><tbody> 不是为“视觉分段”而加,而是按业务逻辑划分数据区块,比如季度、状态、权限等级。它让 CSS 隐藏/显示、JS 批量操作、打印分页都具备明确边界。
<ul><li>每个 <code><tbody> 必须包含完整行(<code><tr>),不能跨 <code><tbody> 拆分同一行
<li><code><tbody class="pending"> 和 <code><tbody class="completed"> 这类命名比 <code><tbody id="group1"> 更利于维护
<li>不要为了“看起来有分隔线”而硬加空 <code><tbody>——空 <code><tbody> 仍占文档流,可能破坏斑马纹计数或高度计算
<li>
<code>document.querySelectorAll('tbody') 返回的是全部主体区块,比遍历所有 <tr> 更精准可靠
<h3>tbody 与 CSS/JS 操作的关键约束</h3>
<p><code><tbody> 是样式和脚本操作的天然边界。很多看似“CSS 不生效”或“JS 插入错位”的问题,根源都在它缺失或嵌套错误。
<ul><li>实现固定表头时,<code><thead> 加 <code>position: sticky; top: 0 前,<tbody> 必须设 <code>max-height 并配合 display: block 或外层 <div> 控制溢出
<li>斑马纹必须写成 <code>tbody tr:nth-child(even),如果 <tr> 直接在 <code><table> 下,<code>:nth-child 会把 <thead> 的行也算进去,导致奇偶错乱
<li>动态更新数据最安全的方式是清空并重写 <code>tbody.innerHTML,避免手动 removeChild 漏掉节点或顺序错乱
<tbody> 默认 <code>display: table-row-group,若误设为 block 或 flex,内部 <tr> 会塌陷失效
<h3>tbody 的 DOM 顺序和嵌套规则不能颠倒</h3>
<p>HTML 规范强制要求:<code><thead> → <code><tfoot> → <code><tbody>,三者都必须是 <code><table> 的直接子元素。顺序错乱会导致 <code><tfoot> 在打印时不复现、部分旧浏览器解析异常。
<ul><li><code><tfoot> 写在 <code><tbody> 前,是为了让浏览器在接收大量主体数据前就渲染页脚(流式加载场景)
<li>任何嵌套在 <code><div> 或其他标签内的 <code><thead>/<code><tbody> 都会被解析为无效结构,DOM 中可能被移出或忽略
<li>所有区域列数必须一致:如果 <code><thead> 有 4 个 <code><th>,每个 <code><tbody> 中的 <code><tr> 就必须有且仅有 4 个 <code><td>(或通过 <code>colspan/rowspan 合理合并)
跨行单元格(rowspan)必须严格落在同一 <tbody> 内,否则辅助技术无法关联语义,JS 也无法准确判断行归属。这容易被忽略,但一旦出错,可访问性就断了。</tbody>











