Can we have multiple <tbody> in same <table>?

HtmlHtml TableXhtml

Html Problem Overview


Can we have multiple <tbody> tags in same <table>? If yes then in what scenarios should we use multiple <tbody> tags?

Html Solutions


Solution 1 - Html

Yes you can use them, for example I use them to more easily style groups of data, like this:

<table>
    <thead>
        <tr><th>Customer</th><th>Order</th><th>Month</th></tr>
    </thead>
    <tbody>
        <tr><td>Customer 1</td><td>#1</td><td>January</td></tr>
        <tr><td>Customer 1</td><td>#2</td><td>April</td></tr>
        <tr><td>Customer 1</td><td>#3</td><td>March</td></tr>
    </tbody>
    <tbody>
        <tr><td>Customer 2</td><td>#1</td><td>January</td></tr>
        <tr><td>Customer 2</td><td>#2</td><td>April</td></tr>
        <tr><td>Customer 2</td><td>#3</td><td>March</td></tr>
    </tbody>
    <tbody>
        <tr><td>Customer 3</td><td>#1</td><td>January</td></tr>
        <tr><td>Customer 3</td><td>#2</td><td>April</td></tr>
        <tr><td>Customer 3</td><td>#3</td><td>March</td></tr>
    </tbody>
</table>

thead th { width: 100px; border-bottom: solid 1px #ddd; font-weight: bold; }
tbody:nth-child(odd) { background: #f5f5f5;  border: solid 1px #ddd; }
tbody:nth-child(even) { background: #e5e5e5;  border: solid 1px #ddd; }

You can view an example here. It'll only work in newer browsers, but that's what I'm supporting in my current application, you can use the grouping for JavaScript etc. The main thing is it's a convenient way to visually group the rows to make the data much more readable. There are other uses of course, but as far as applicable examples, this one is the most common one for me.

Solution 2 - Html

Yes. From the DTD

<!ELEMENT table
     (caption?, (col*|colgroup*), thead?, tfoot?, (tbody+|tr+))>

So it expects one or more. It then goes on to say

> Use multiple tbody sections when rules > are needed between groups of table > rows.

Solution 3 - Html

According to this example from the spec it can be done: w3-struct-tables.

> Table rows may be grouped into a table head, table foot, and one or more table body sections, using the THEAD, TFOOT and TBODY elements, respectively.

Solution 4 - Html

Martin Joiner's problem is caused by a misunderstanding of the <caption> tag.

The <caption> tag defines a table caption.

The <caption> tag must be the first child of the <table> tag.

You can specify only one caption per table.

Also, note that the scope attribute should be placed on a <th> element and not on a <tr> element.

The proper way to write a multi-header multi-tbody table would be something like this :

This is the only correct place to put a caption.
First Half of Table (British Dinner)
1 Fish
2 Chips
3 Peas
4 Gravy
Second Half of Table (Italian Dinner)
5 Pizza
6 Salad
7 Oil
8 Bread

Solution 5 - Html

Yes. I use them for dynamically hiding/revealing the relevant part of a table, e.g. a course. Viz.

<table>
  <tbody id="day1" style="display:none">
    <tr><td>session1</td><tr>
    <tr><td>session2</td><tr>
  </tbody>
  <tbody id="day2">
    <tr><td>session3</td><tr>
    <tr><td>session4</td><tr>
  </tbody>
  <tbody id="day3" style="display:none">
    <tr><td>session5</td><tr>
    <tr><td>session6</td><tr>
  </tbody>
</table>

A button can be provided to toggle between everything or just the current day by manipulating tbodies without processing many rows individually.

Solution 6 - Html

EDIT: The caption tag belongs to table and thus should only exist once. Do not associate a caption with each tbody element like I did:

<table>
    <caption>First Half of Table (British Dinner)</caption>
    <tbody>
        <tr><th>1</th><td>Fish</td></tr>
        <tr><th>2</th><td>Chips</td></tr>
        <tr><th>3</th><td>Pease</td></tr>
        <tr><th>4</th><td>Gravy</td></tr>
    </tbody>
    <caption>Second Half of Table (Italian Dinner)</caption>
    <tbody>
        <tr><th>5</th><td>Pizza</td></tr>
        <tr><th>6</th><td>Salad</td></tr>
        <tr><th>7</th><td>Oil</td></tr>
        <tr><th>8</th><td>Bread</td></tr>
    </tbody>
</table>

BAD EXAMPLE ABOVE: DO NOT COPY

The above example does not render as you would expect because writing like this indicates a misunderstanding of the caption tag. You would need lots of CSS hacks to make it render correctly because you would be going against standards.

I searched for W3Cs standards on the caption tag but could not find an explicit rule that states there must be only one caption element per table but that is in fact the case.

Solution 7 - Html

I have created a JSFiddle where I have two nested ng-repeats with tables, and the parent ng-repeat on tbody. If you inspect any row in the table, you will see there are six tbody elements, i.e. the parent level.

HTML

<div>
        <table class="table table-hover table-condensed table-striped">
            <thead>
                <tr>
                    <th>Store ID</th>
                    <th>Name</th>
                    <th>Address</th>
                    <th>City</th>
                    <th>Cost</th>
                    <th>Sales</th>
                    <th>Revenue</th>
                    <th>Employees</th>
                    <th>Employees H-sum</th>
                </tr>
            </thead>
            <tbody data-ng-repeat="storedata in storeDataModel.storedata">
                <tr id="storedata.store.storeId" class="clickableRow" title="Click to toggle collapse/expand day summaries for this store." data-ng-click="selectTableRow($index, storedata.store.storeId)">
                    <td>{{storedata.store.storeId}}</td>
                    <td>{{storedata.store.storeName}}</td>
                    <td>{{storedata.store.storeAddress}}</td>
                    <td>{{storedata.store.storeCity}}</td>
                    <td>{{storedata.data.costTotal}}</td>
                    <td>{{storedata.data.salesTotal}}</td>
                    <td>{{storedata.data.revenueTotal}}</td>
                    <td>{{storedata.data.averageEmployees}}</td>
                    <td>{{storedata.data.averageEmployeesHours}}</td>
                </tr>
                <tr data-ng-show="dayDataCollapse[$index]">
                    <td colspan="2">&nbsp;</td>
                    <td colspan="7">
                        <div>
                            <div class="pull-right">
                                <table class="table table-hover table-condensed table-striped">
                                    <thead>
                                        <tr>
                                            <th></th>
                                            <th>Date [YYYY-MM-dd]</th>
                                            <th>Cost</th>
                                            <th>Sales</th>
                                            <th>Revenue</th>
                                            <th>Employees</th>
                                            <th>Employees H-sum</th>
                                        </tr>
                                    </thead>
                                    <tbody>
                                        <tr data-ng-repeat="dayData in storeDataModel.storedata[$index].data.dayData">
                                            <td class="pullright">
                                                <button type="btn btn-small" title="Click to show transactions for this specific day..." data-ng-click=""><i class="icon-list"></i>
                                                </button>
                                            </td>
                                            <td>{{dayData.date}}</td>
                                            <td>{{dayData.cost}}</td>
                                            <td>{{dayData.sales}}</td>
                                            <td>{{dayData.revenue}}</td>
                                            <td>{{dayData.employees}}</td>
                                            <td>{{dayData.employeesHoursSum}}</td>
                                        </tr>
                                    </tbody>
                                </table>
                            </div>
                        </div>
                    </td>
                </tr>
            </tbody>
        </table>
    </div>

( Side note: This fills up the DOM if you have a lot of data on both levels, so I am therefore working on a directive to fetch data and replace, i.e. adding into DOM when clicking parent and removing when another is clicked or same parent again. To get the kind of behavior you find on Prisjakt.nu, if you scroll down to the computers listed and click on the row (not the links). If you do that and inspect elements you will see that a tr is added and then removed if parent is clicked again or another. )

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJitendra VyasView Question on Stackoverflow
Solution 1 - HtmlNick CraverView Answer on Stackoverflow
Solution 2 - HtmlMartin SmithView Answer on Stackoverflow
Solution 3 - HtmlKris van der MastView Answer on Stackoverflow
Solution 4 - HtmlJohn SlegersView Answer on Stackoverflow
Solution 5 - HtmlCPslashMView Answer on Stackoverflow
Solution 6 - HtmlMartin JoinerView Answer on Stackoverflow
Solution 7 - HtmlPixicView Answer on Stackoverflow