Can we have multiple <tbody> in same <table>?
Asked Answered
E

7

655

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

Ephedrine answered 19/6, 2010 at 18:25 Comment(1)
In addition, if you run a HTML document with multiple <tbody> tags through W3C's HTML Validator, with a HTML5 DOCTYPE, it will successfully validate.Fodder
Z
784

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

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; }
<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>

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.

Zaremski answered 19/6, 2010 at 18:45 Comment(10)
ok thanks for great answer. Does is matter to screen reader , one tbody or multiple?Ephedrine
@metal-gear-solid - In my experience they handle them fine, e.g.: as if they were one <tbody>. When you start to nest tables, that's what usually gives real navigation problems for a screen reader.Zaremski
So use of <tbody> is just for visual. there is no semantic difference between one <tbody> and multiple <tbody>Ephedrine
@metal: no, there is a semantic difference - multiple <tbody> elements describes separate groups in the table, as was explained in the answer. Also I should add that it's generally better to target cells for backgrounds, so the CSS should be, for example, tbody:nth-child(odd) td { background: #f5f5f5; }Campestral
What is the definition of "newer browsers"?Bathetic
@TimDown - when I said "newer browsers" it was only referring to the CSS :nth-child() usage for the linked demonstration, the multiple <tbody> will work in any browser.Zaremski
This is a pretty straightforward idea, but what to do when you need some kind of hierarchy, 3-7 levels deep? You're not supposed to embed TBODY elements into each other, right?Cathead
@LaszloTenki No. You can, however, nest entire tables.Rogation
Logically and semantically, having multiple TBODY elements in a TABLE does not make sense.Lasagne
I used this approach in an attempt to keep groups of rows together when printing the page, using tbody { page-break-inside: avoid; }. Note however that this doen not work in Chrome. The reason is explained in Page break not working with tbody issue and has to do with a tbody not being a block element, by default.Pronouncement
S
312

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.

Spotted answered 19/6, 2010 at 18:29 Comment(1)
As of the HTML5 spec, this changes slightly, but the fundamental "yes, multiple tbody elements are fine) remains. Specifically, you're now allowed to put the one tfoot element after the tbody if you like. (They neatly side-stepped the DTD aspect by saying they don't provide one.) :-)Numerous
F
52

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.

Fleeta answered 19/6, 2010 at 18:28 Comment(0)
F
17

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 :

<table id="dinner_table">
    <caption>This is the only correct place to put a caption.</caption>
    <tbody>
        <tr class="header">
            <th colspan="2" scope="col">First Half of Table (British Dinner)</th>
        </tr>
        <tr>
            <th scope="row">1</th>
            <td>Fish</td>
        </tr>
        <tr>
            <th scope="row">2</th>
            <td>Chips</td>
        </tr>
        <tr>
            <th scope="row">3</th>
            <td>Peas</td>
        </tr>
        <tr>
            <th scope="row">4</th>
            <td>Gravy</td>
        </tr>
    </tbody>
    <tbody>
        <tr class="header">
            <th colspan="2" scope="col">Second Half of Table (Italian Dinner)</th>
        </tr>
        <tr>
            <th scope="row">5</th>
            <td>Pizza</td>
        </tr>
        <tr>
            <th scope="row">6</th>
            <td>Salad</td>
        </tr>
        <tr>
            <th scope="row">7</th>
            <td>Oil</td>
        </tr>
        <tr>
            <th scope="row">8</th>
            <td>Bread</td>
        </tr>
    </tbody>
</table>
Feminine answered 31/5, 2013 at 13:31 Comment(1)
Spec recommends using scope="rowgroup" (instead of col) for the tbody headers. See Example.Figural
R
10

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.

Rathe answered 6/3, 2012 at 13:47 Comment(0)
B
2

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.

Bindle answered 15/5, 2013 at 14:7 Comment(0)
J
2

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. )

Jemima answered 31/7, 2013 at 15:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.