Topic 3.2
Table Structure
In one line
A well-built table has a <caption> that names it, row groups (<thead>, <tbody>, <tfoot>) that separate headers, data and totals, and optional <colgroup>/<col> elements that style whole columns.
Think of it like this
A shop receipt. At the top it says what it is ("Receipt, Green Grocers, 12 March"): that's the caption. Then a line of column titles (Item, Qty, Price): the head. Then the items you bought: the body. At the bottom, the total: the foot. You'd never mix the total in among the items, and a table shouldn't either.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- caption
- The visible title of a table, written as the first thing inside it.
- Accessible name
- The name a screen reader announces for an element. For a table, the caption gives it one.
- Row group
- A set of rows treated together: the head, a body, or the foot of a table.
- thead
- The row group with the header rows.
- tbody
- A row group with data rows. A table can have several.
- tfoot
- The row group with summary rows, like totals. Always drawn at the bottom.
- colgroup / col
- Elements that stand for columns, used to set widths and backgrounds for a whole column at once.
- span (on col)
- How many columns one
<col>or<colgroup>stands for.
Step by step
01Name the table
Start with a caption, the first thing inside <table>. It says what the data is about, in a few words.
<table>
<caption>Receipt: Green Grocers, 12 March</caption>
</table>02Put the header row in thead
The row of column titles goes into <thead>. Its cells are <th>.
<table>
<caption>Receipt: Green Grocers, 12 March</caption>
<thead>
<tr><th>Item</th><th>Qty</th><th>Price</th></tr>
</thead>
</table>03Put the data in tbody
Each item becomes a row in <tbody>. The item name labels its row, so it is a <th>.
<tbody>
<tr><th>Rice, 5 kg</th><td>1</td><td>₹350</td></tr>
<tr><th>Milk, 1 L</th><td>2</td><td>₹130</td></tr>
</tbody>04Put the total in tfoot
The total row goes last, in <tfoot>. Its label spans the first two columns, using colspan (explained fully in Topic 3.3).
<tfoot>
<tr><th colspan="2">Total</th><td>₹480</td></tr>
</tfoot>05Describe the columns
Add a <colgroup> right after the caption. Here the first column gets a width and the price column a soft background. col elements are empty: they have no closing tag and hold no content.
<colgroup>
<col style="width: 12em">
<col>
<col class="money">
</colgroup>Try it yourself
- 1
Move the caption
In the receipt example, add
caption { caption-side: bottom; }to the CSS. Predict: does the caption move in the HTML, or only on screen? Inspect it to check. - 2
Add a section
In the rainfall example, add a third
<tbody>with October (90 mm). Predict the new values ofBody sections,Total rowsandFooter row indexbefore you run. - 3
Find the column rule
In the last example, move
color: redfrom.highlightto a new ruletd:nth-child(2) { color: red; }. Now the numbers turn red: styling the cells works where styling the column didn't.
Code & diagrams
JavaScript can reach each part directly: `caption`, `tHead`, `tBodies` and `tFoot`. The green column comes from the `col` element.
Preview
Console
Nothing logged yet.Expected console output
Caption: Receipt: Green Grocers, 12 March
Header rows: 1
Body rows: 3
Footer total: ₹540The footer is written before the bodies (old HTML 4 style), yet it is drawn last and its `rowIndex` is the last one. Write it last anyway: that's today's rule.
Preview
Console
Nothing logged yet.Expected console output
Body sections: 2
Total rows: 6
Footer row index: 5The second column turns yellow and wider, but its text stays black and left-aligned. Only `background`, `width`, `border` and `visibility` work on columns.
Preview
Console
Nothing logged yet.Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Put the caption outside the table
In the receipt example, move <caption>…</caption> to just above <table>, and run.
Break #2
Style text through a column
Add <col style="text-align: right"> for the price column and expect the prices to line up right.
Myth vs fact
Myth
A <caption> is the same as a heading above the table.
Fact
A heading isn't tied to the table. A caption belongs to it: screen readers announce it as the table's name, and it moves with the table.
Myth
A table can only have one <tbody>.
Fact
It can have as many as you need. Several bodies split the data into sections, each of which CSS and JavaScript can treat separately.
Myth
tfoot must go before tbody.
Fact
That was HTML 4. The current standard puts tfoot last; browsers draw it at the bottom either way.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
table.rowsliststheadrows first, then body rows in source order, thentfootrows, which is why the footer'srowIndexis always last. DOM helpers liketable.createTHead(),createTFoot(),createCaption()andinsertRow()put new parts in the correct position for you. - ▸
When printing, Chromium and Firefox repeat the
theadat the top of each page of a long table (support for repeatingtfootis less consistent). Keeping headers intheadis what makes this possible. - ▸
Columns can be hidden with
visibility: collapseon acol: the column disappears and the table shrinks, without changing the HTML. For a static table, plain CSS on the cells (td:nth-child(n)) is often simpler and better supported. - ▸
Use a
figurewith afigcaptionaround a table only if you need the figure semantics (for example a numbered "Table 3" referred to from text); otherwisecaptionis the right, simpler name for the table.
Remember this
- 1
<caption>gives the table a visible title. It must be the first child of<table>. Screen readers use it as the table's accessible name (the name announced when someone reaches the table, like "table, Exam marks 2026, 4 columns, 5 rows"), so it helps people pick the right table on a page with several. Style its position with CSScaption-side: bottomif you want it below. - 2
Rows are grouped into row groups.
<thead>holds the header row or rows.<tbody>holds the data rows; a table may have several<tbody>elements to split data into sections (for example one per month).<tfoot>holds summary rows such as totals. A table has at most onetheadand onetfoot. - 3
The order inside
<table>is fixed by the HTML standard:captionfirst, then anycolgroups, thenthead, then thetbodyelements (or plain rows), thentfootlast. Older HTML 4 allowedtfootbeforetbody, and browsers still draw such a footer at the bottom, but today's rule is to write it last. - 4
Row groups are useful beyond meaning. CSS can target them (
thead th,tbody tr:nth-child(even)for stripes), JavaScript gets them directly (table.tHead,table.tBodies,table.tFoot), and when a long table is printed, browsers can repeat thetheadat the top of every page. - 5
<colgroup>and<col>describe columns, which otherwise exist only through the cells. They come right after the caption.<col span="2">stands for two columns. Their main use is styling: you can setwidth,background,border(withborder-collapse: collapse) andvisibility: collapseon a column. Other properties, likecolorortext-align, are not applied to the cells throughcol; style the cells for those. - 6
Since
<tbody>is added for you when missing, the question is never "does my table have a tbody?" (it always does once parsed) but "did I put the header rows in atheadand the totals in atfoot?" That's the part only you can do.
Explain it without notes
What does each of caption, thead, tbody and tfoot do, and in what order are they written?
Why can't you right-align text in a column with <col style="text-align: right">?
When would you use more than one <tbody>?
Practice
Build a small expense table for a trip with a caption, a thead (Item, Cost), three items in tbody and a total in tfoot.
Give the Cost column a light background using colgroup/col, and right-align the costs.
Trade-offs
- ↔
colstyling is short (one element styles a whole column) but limited to four properties; cell selectors liketd:nth-child(3)can do anything but must be kept in sync if columns move. - ↔
Several
tbodysections add structure and styling hooks, at the cost of a little more markup; for a short flat list of rows, one body is enough.
Done when you can
I can give a table a caption in the right place.
I can split rows into
thead, one or moretbody, andtfoot, in the correct order.I can use
colgroupandcolwithspanto size and colour columns.I know which CSS properties work on columns and which don't.
I can reach a table's parts from JavaScript with
caption,tHead,tBodiesandtFoot.