Command Palette

Search for a command to run...

Hectal
PHASE 3Beginner ~20 min· topic 1 of 4

Topic 3.1

Table Basics

In one line

An HTML table is built row by row: <table> holds rows (<tr>), each row holds cells, and a cell is either a data cell (<td>) or a header cell (<th>) that labels other cells.

Think of it like this

A school attendance register. The page is the table. Each line across, one per student, is a row. Each box on that line is a cell. The boxes along the top that say "Name", "Monday", "Tuesday" are headers: they aren't data themselves, they tell you what the boxes below them mean.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Table
A grid of rows and columns for data that makes sense read both across and down.
Tabular data
Information where each value belongs to one row and one column at the same time, like a mark for one student in one subject.
tr
Table row: one horizontal line of cells.
td
Table data: a cell that holds a value.
th
Table header: a cell that labels a row or a column.
Cell
One box in the grid, where one row and one column meet.
Source order
The order elements are written in the HTML file. Tables are read row by row in this order.
Foster parenting
The browser moving content that isn't allowed inside a table (like loose text) to just before the table.

Step by step

01Start with the table

Everything lives between <table> and </table>. On its own, an empty table shows nothing at all.

marks.htmlwhole filehtml
<table>

</table>

02Add a header row

The first row labels the columns, so its cells are <th>. Each column will hold one kind of information: the student's name, then each subject.

marks.htmlwhole filehtml
<table>
  <tr>
    <th>Student</th>
    <th>Maths</th>
    <th>Science</th>
  </tr>
</table>

03Add data rows

Each student gets a row. The first cell names the row, so it is also a <th>: it labels the marks next to it. The marks are <td>.

Every row has the same number of cells (three here), so the columns line up.

marks.htmlwhole filehtml
<table>
  <tr>
    <th>Student</th>
    <th>Maths</th>
    <th>Science</th>
  </tr>
  <tr>
    <th>Asha</th>
    <td>92</td>
    <td>88</td>
  </tr>
  <tr>
    <th>Ravi</th>
    <td>75</td>
    <td>81</td>
  </tr>
</table>
Add data rowsdiagram
Rendering diagram…

04Make the grid visible

Add three CSS rules: borders on the cells, border-collapse: collapse so neighbouring borders merge into one line, and padding so the text doesn't touch the lines.

style.csswhole filecss
table { border-collapse: collapse; }
th, td {
  border: 1px solid #999;
  padding: 6px 12px;
}

05Check what the browser built

Open DevTools → Elements and expand the table. You'll see a <tbody> you never wrote, wrapping your rows. That is the parser following the rule "rows live in a row group". It changes nothing visible, but it matters for CSS selectors like table > tr, which now match nothing.

Try it yourself

  1. 1

    Add a row

    In the first example, predict what Rows: will print if you add a row for Mango, Orange, ₹80. Add it and run. Did the price column line up by itself?

  2. 2

    Turn data into a header

    In the first example, change the <td> around each fruit name to <th>. The names become bold and centred. Inspect one: it is now a header cell that labels its row.

  3. 3

    Remove border-collapse

    Delete border-collapse: collapse; and run. Predict first: will you see one line or two between cells? Each cell draws its own border, with 2px of space between them.

Code & diagrams

A first table New tab

You never wrote `<tbody>`, but the browser added one. Delete the CSS tab to see a table's default look: no lines at all.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

Rows: 4
Cells in row 1: 3
tbody elements added by the browser: 1
The order a table is read in New tab

Row by row, left to right: the same order a screen reader uses when it reads the table straight through.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

Day > Breakfast > Lunch > Monday > Poha > Dal rice > Tuesday > Idli > Rajma
Text outside a cell is moved above the table New tab

The stray sentence was written after the row, yet it appears above the table. This is foster parenting.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

Text inside the table: I am inside a cell
Text just before the table: Oops, I am not in a cell

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

Forget the tr

In the first example, delete the <tr> and </tr> around the Banana row's three cells.

terminal
$ the page shows
── what you'll see ──
Banana, Yellow and ₹10 still appear on their own row (the parser started a row for them), but in DevTools the row you wrote is gone and a new <tr> was created.

Break #2

Put a heading inside the table

Add <h2>Fruit prices</h2> between <table> and the first <tr>.

terminal
$ the page shows
── what you'll see ──
The heading appears above the table, outside it. DevTools shows <h2> as a sibling before <table>.

Myth vs fact

Myth

th is just a bold td.

Fact

The bold look is only a default style. th means "this labels other cells", and screen readers announce it with each data cell. Making a td bold with CSS gives none of that.

Myth

You need a column element to make columns.

Fact

Columns come from the cells: the nth cell of each row forms column n. (<col> exists, but only to style or group columns; Topic 3.2.)

Myth

Tables are old-fashioned and should be avoided.

Fact

Tables were misused for page layout in the 1990s. For data, they are still the right, accessible element, and nothing else gives screen readers row and column navigation for free.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    The parser has special insertion modes for tables ("in table", "in table body", "in row", "in cell"). Foster parenting, the implied <tbody> and implied <tr> all come from these rules in the HTML Living Standard, so every browser repairs a broken table the same way.

  • ▸

    Because of the implied <tbody>, table > tr matches nothing in a parsed page. Write table tr or tbody > tr. In JavaScript, table.rows, row.cells, row.rowIndex and cell.cellIndex give fast access without querying.

  • ▸

    Table layout is costly: by default (table-layout: auto) the browser may need every cell's content before it can fix column widths, so very large tables can lay out slowly. table-layout: fixed sizes columns from the first row and widths alone, which is faster and steadier (see the CSS course).

  • ▸

    Closing tags </td>, </th> and </tr> are optional in HTML: the next cell or row closes the previous one. Writing them anyway keeps the structure obvious and avoids surprises when you copy rows around.

Remember this

  1. 1

    A table is for tabular data: information where each value belongs to both a row and a column, so reading across and reading down both make sense. "Asha's mark in Maths" is found where the Asha row meets the Maths column. If your content doesn't have that two-way meaning, it isn't a table (Topic 3.4 covers what to use instead).

  2. 2

    The <table> element wraps the whole thing. Inside it, each <tr> (table row) is one horizontal line. HTML has no element for a column: columns appear by themselves, because the first cell of every row lines up, then the second cell of every row, and so on.

  3. 3

    Inside a row go the cells. <td> (table data) holds a value: a number, a word, even a link or an image. <th> (table header) holds a label for other cells. Browsers show th text bold and centred by default, but that look is not the point: th tells screen readers "this cell names a row or column", and they read it aloud when you move between data cells.

  4. 4

    Browsers read a table in source order: the first row from left to right, then the second row, and so on (right to left in right-to-left languages like Arabic). A screen reader that reads the table straight through follows that same order, so put headers first: the header row at the top, and the row header as the first cell of each row.

  5. 5

    The parser (the part of the browser that turns your HTML text into elements) is strict about tables. Only rows may sit directly in a table, and only cells may sit directly in a row. Text or other elements placed between cells are moved out and drawn above the table, a repair called foster parenting. The parser also wraps loose rows in a <tbody> element for you, which you'll meet in Topic 3.2.

  6. 6

    By default a table is only as wide as its content needs, its cells have no visible borders, and there are 2 pixels of space between cells. All of that is CSS: border, border-collapse: collapse (to merge the double lines into one) and padding on the cells are the usual first three rules. Don't use the old border attribute on <table>; it is obsolete, and CSS does the job better.

Explain it without notes

01

What is the difference between td and th, beyond how they look?

02

There is no column element in a basic table. How does a browser know which cells form a column?

03

What is foster parenting, and when does it happen?

04

When is a table the right choice for content?

Practice

01

Build a 3-column table of three countries with their capital and continent, with a header row.

02

Make the country names row headers and add borders that show as single lines.

Trade-offs

  • ↔

    A table gives the strongest structure for two-way data (screen reader navigation, copy-paste into spreadsheets), but it is rigid: on narrow screens wide tables need extra work (Topic 3.4).

  • ↔

    Leaving out optional closing tags makes the HTML shorter but harder to read and edit; most teams write them all.

Done when you can

  • I can build a table from table, tr, td and th.

  • I can decide which cells should be headers and which are data.

  • I can explain the order a table is read in.

  • I know why the browser adds a <tbody> and moves stray content out of a table.

  • I can add borders, padding and border-collapse so the grid is readable.