By Jitendra Vyas


2010-06-19 18:25:10 8 Comments

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

8 comments

@Nick Craver 2010-06-19 18:45:14

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.

@Jitendra Vyas 2010-06-19 18:49:32

ok thanks for great answer. Does is matter to screen reader , one tbody or multiple?

@Nick Craver 2010-06-19 18:53:17

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

@Jitendra Vyas 2010-06-20 03:36:20

So use of <tbody> is just for visual. there is no semantic difference between one <tbody> and multiple <tbody>

@DisgruntledGoat 2010-06-21 12:40:30

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

@Tim Down 2012-12-17 11:07:34

What is the definition of "newer browsers"?

@Nick Craver 2012-12-17 14:10:57

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

@Lucas Holt 2013-02-20 16:37:13

I have found that IE8 will crash if you try to hide a tbody using CSS or in a less direct method like jQuery's hide().

@Timo Huovinen 2013-10-29 13:22:50

@LucasHolt it does not crash in IE Tester IE8, can you confirm that it still crashes in IE8?

@Lucas Holt 2013-10-29 17:21:14

@TimoHuovinen I've seen this occur with IE8 on Windows 7 with several versions of jQuery. It does not happen with IE9 in compatibility view though.

@mshthn 2017-01-25 14:31:01

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?

@Mr Lister 2017-03-09 09:16:21

@LaszloTenki No. You can, however, nest entire tables.

@user1451111 2017-07-21 11:38:31

Logically and semantically, having multiple TBODY elements in a TABLE does not make sense.

@forgottofly 2018-01-19 10:11:17

In this case how to provide scroll bar in <tbody> by keeping header constant?

@R. Schreurs 2018-03-28 10:58:06

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.

@John Slegers 2013-05-31 13:31:08

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>

@Cypher 2014-01-16 07:53:41

The caption tag should follow the opening table tag. developer.mozilla.org/en-US/docs/Web/HTML/Element/table

@John Slegers 2014-01-21 09:59:37

You're right. I somehow misinterpreted the docs. I fixed the error.

@Kris van der Mast 2010-06-19 18:28:50

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

@Martin Joiner 2013-05-15 14:07:23

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.

@Pixic 2013-07-31 15:56:18

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

@Bern 2012-08-15 08:18:30

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.

@CPslashM 2012-03-06 13:47:56

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.

@Martin Smith 2010-06-19 18:29:28

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.

@T.J. Crowder 2013-07-11 17:51:31

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

@KernelCurry 2014-09-21 19:13:00

Thanks for this response. Referencing specs is the #1 answer in my book.

@Alexis Wilke 2016-01-28 06:23:20

So it expects one or more. This is wrong, it may be a set of <tr> so it could also be zero (i.e. a tbody or a tr means it could just be a tr and no tbody.)

@Gecko IT 2016-11-02 14:26:14

@AlexisWilke this is true according to the specs: The TBODY start tag is always required except when the table contains only one table body and no table head or foot sections

Related Questions

Sponsored Content

37 Answered Questions

[SOLVED] Add table row in jQuery

36 Answered Questions

27 Answered Questions

[SOLVED] Set cellpadding and cellspacing in CSS?

22 Answered Questions

[SOLVED] Where should I put <script> tags in HTML markup?

34 Answered Questions

[SOLVED] RegEx match open tags except XHTML self-contained tags

  • 2009-11-13 22:38:26
  • Jeff
  • 2735481 View
  • 1363 Score
  • 34 Answer
  • Tags:   html regex xhtml

19 Answered Questions

[SOLVED] Is it possible to apply CSS to half of a character?

  • 2014-05-09 16:16:57
  • Mathew MacLean
  • 237849 View
  • 2752 Score
  • 19 Answer
  • Tags:   javascript html css

31 Answered Questions

[SOLVED] Make a div fill the height of the remaining screen space

  • 2008-09-18 05:06:17
  • Vincent McNabb
  • 939365 View
  • 1797 Score
  • 31 Answer
  • Tags:   html css html-table

13 Answered Questions

[SOLVED] How do I retrieve an HTML element's actual width and height?

Sponsored Content