Follow up to #14911
When a table with multiple <tbody> elements is loaded into the editor, all body rows are merged into a single <tbody> on output. The current table model stores body rows as a flat sequence of elements with no representation of <tbody> boundaries, so the conversion pipeline normalizes every table body to one <tbody> regardless of the input structure.
Multiple <tbody> sections are valid HTML spec and are the standard mechanism for grouping rows into logical sections — for instance, organizing data under scope="rowgroup" headers as shown in the W3C WAI Tables Tutorial. Screen readers rely on these boundaries to announce rowgroup context, and losing them degrades table navigation for assistive technology users. It also constitutes data loss, since the original grouping cannot be reconstructed from the collapsed output.
It is also worth noting that while the spec's content model places <thead> before any <tbody> elements, the HTML parser's error recovery handles <thead> tokens encountered inside a table body by closing the current <tbody> and processing the <thead> normally. This means real-world HTML can contain a <thead> appearing between <tbody> sections, and browsers will produce a valid DOM tree from it. The model should account for this when processing tables from external or legacy sources.
📝 Provide detailed reproduction steps (if any)
- Load the following HTML into the editor (via source editing or editor.setData()):
<table>
<thead>
<tr>
<th></th>
<th>2008</th>
<th>2007</th>
<th>2006</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="rowgroup">Research and development</th>
<td>$ 1,109</td>
<td>$ 782</td>
<td>$ 712</td>
</tr>
<tr>
<th scope="row">Percentage of net sales</th>
<td>3.4%</td>
<td>3.3%</td>
<td>3.7%</td>
</tr>
</tbody>
<tbody>
<tr>
<th scope="rowgroup">Selling, general, and administrative</th>
<td>$ 3,761</td>
<td>$ 2,963</td>
<td>$ 2,433</td>
</tr>
<tr>
<th scope="row">Percentage of net sales</th>
<td>11.6%</td>
<td>12.3%</td>
<td>12.6%</td>
</tr>
</tbody>
</table>
- Call editor.getData() or switch to source view.
✔️ Expected result
Two <tbody> sections in the output, matching the input.
❌ Actual result
All body rows are wrapped in a single <tbody>.
If you'd like to see this fixed sooner, add a 👍 reaction to this post.
Follow up to #14911
When a table with multiple
<tbody>elements is loaded into the editor, all body rows are merged into a single<tbody>on output. The current table model stores body rows as a flat sequence of elements with no representation of<tbody>boundaries, so the conversion pipeline normalizes every table body to one<tbody>regardless of the input structure.Multiple
<tbody>sections are valid HTML spec and are the standard mechanism for grouping rows into logical sections — for instance, organizing data underscope="rowgroup"headers as shown in the W3C WAI Tables Tutorial. Screen readers rely on these boundaries to announce rowgroup context, and losing them degrades table navigation for assistive technology users. It also constitutes data loss, since the original grouping cannot be reconstructed from the collapsed output.It is also worth noting that while the spec's content model places
<thead>before any<tbody>elements, the HTML parser's error recovery handles<thead>tokens encountered inside a table body by closing the current<tbody>and processing the<thead>normally. This means real-world HTML can contain a<thead>appearing between<tbody>sections, and browsers will produce a valid DOM tree from it. The model should account for this when processing tables from external or legacy sources.📝 Provide detailed reproduction steps (if any)
✔️ Expected result
Two
<tbody>sections in the output, matching the input.❌ Actual result
All body rows are wrapped in a single
<tbody>.If you'd like to see this fixed sooner, add a 👍 reaction to this post.