14

14Tables and Footnotes

14.1

The basics of tables

We haven’t looked at tables before in any context so let’s get a basic idea of how they look and how they work.

Tables are defined with the <table>...</table> element. Each row of a table is enclosed in <tr>...</tr> elements and these are stacked one on top of the other. Within a given row, each cell is enclosed in either <th>...</th> tags or <td>...</td> tags.

The <th> tags contain a single cell that acts as a header (table header is what it stands for) or label for the column. The <td> tags (table data) are standard cells within a table that contain some information.

Look at the following table, this is a very similar to a table in Word:

mileagebirminghamliverpoollondonmanchesternewcastle
birmingham0113126221146
liverpool113022134174
london1262210209283
manchester87342090145
newcastle1461742831450

This table has 6 rows and each row has 6 cells (it’s a 6×6 table). The top row is acting as a header for each of the columns and the first cell in each row is also a header.

At the most basic level, the HTML it would be constructed as follows:

If we were to build this very basic table, the HTML behind it would be:

Basic table HTML
<table>
    <tr>
        <th>Mileage</th><th>Birmingham</th><th>Liverpool</th><th>London</th><th>Manchester</th><th>Newcastle</th>
    </tr>
    <tr>
        <th>Birmingham</th><td>0</td><td>113</td><td>126</td><td>87</td><td>146</td>
    </tr>
    <tr>
        <th>Liverpool</th><td>0113</td><td>0</td><td>221</td><td>34</td><td>174</td>
    </tr>
    <tr>
        <th>London</th><td>126</td><td>221</td><td>0</td><td>209</td><td>283</td>
    </tr>
    <tr>
        <th>Manchester</th><td>87</td><td>34</td><td>209</td><td>0</td><td>145</td>
    </tr>
    <tr>
        <th>Newcastle</th><td>146</td><td>174</td><td>283</td><td>145</td><td>0</td>
    </tr>
</table>
Code 14.1   Basic table HTML

And it looks like this (it’s not much to write home about):

MileageBirminghamLiverpoolLondonManchesterNewcastle
Birmingham011312687146
Liverpool0113022134174
London1262210209283
Manchester87342090145
Newcastle1461742831450

Although it’s very basic, you can see that with a bit of tidying up it will do what we want of it.

There are a few of things to note:

Firstly, the table has been drawn without borders; this wasn’t always the case, in earlier browsers and version of HTML, tables were drawn with borders by default.

Secondly, there is a difference between entries contained within <th> elements, these are in a bold font and those in a <td> element, these are in a regular font.

Thirdly, all the rows have exactly the same number of cells (six in this case).

Now this last point isn’t an absolute rule, different numbers of cells within different rows is perfectly valid in terms of the HTML; it will just shuffle the cells up next to each other and leave a gap at the end of the row.

However, having different number of cells in different rows is really confusing to work with, so there is a rule as far as we are concerned:

the Gledhill rule for tables

Always have the same number of cells on each row within a table

Sometimes it is necessary to merge two or more cells and there are techniques for doing this that I cover later in the section. When defining the table layout always start with the same number of cells per row.

There are also some problems with this table: it’s not very even, the cells are not all the same width, the column with London at the top is narrower than the one with Birmingham.

The centring of entries within a cell is inconsistent, some are centred within the cell (the city name in the column on the left are centred) and some are left justified (the ones with the numbers).

That said, tables are very easy to understand, the following explains the basic construction of any table within the HTML:

14.1.1

HTML table construction

The entire table is contained within a <table>...</table> element:

<table>
</table>

Each row within the table is contained within <tr>...</tr> elements (for 3 rows, there are 3 <tr> elements):

<table>
    <tr>   <!-- Row 1 -->
    </tr>
    <tr>   <!-- Row 2 -->
    </tr>
    <tr>   <!-- Row 3 -->
    </tr>
</table>

Within each row there are a fixed number of cells. These cells can be any mixture of <th> or <td> cells, generally, the first row of a table is usually a title row, giving the names or purpose of the columns, a title row will use <th> (table heading) elements and the data contained in the remaining rows will be in <td> (table data) elements. Again, this is not a hard and fast rule, in the table above; the first cell in each row is also a title and would be a <th> element.

Let’s assume we want each row to have 2 cells and that the first row is a heading row. Now we would have:

<table>
    <tr>   <!-- Row 1 -->
        <th>Col 1 Heading</th>
        <th>Col 2 Heading</th>
    </tr>
    <tr>   <!-- Row 2 -->
    </tr>
    <tr>   <!-- Row 3 -->
    </tr>
</table>

Let’s populate the rest of the table listing the row and column position of the cell:

<table>
    <tr>   <!-- Row 1 -->
        <th>Col 1 Heading</th>
        <th>Col 2 Heading</th>
    </tr>
    <tr>   <!-- Row 2 -->
        <td>R2C1</td>
        <td>R2C2</td>
    </tr>
    <tr>   <!-- Row 3 -->
        <td>R3C1</td>
        <td>R3C2</td>
    </tr>
</table>

This would give us the following:

Col 1 Heading Col 2 Heading
R2C1 R2C2
R3C1 R3C2

You can see how it works. It’s straight forward. Declare a table with <table> put in however many rows you want, each with a <tr> element and then add the same number of cells to each row with either <th> or <td> elements.

14.1.2

Adding table borders and border collapse

Let’s add a border to the cells:

<table>
    <tr>   <!-- Row 1 -->
        <th style="border: 1px solid black">Col 1 Heading</th>
        <th style="border: 1px solid black">Col 2 Heading</th>
    </tr>
    <tr>   <!-- Row 2 -->
        <td style="border: 1px solid black">R2C1</td>
        <td style="border: 1px solid black">R2C2</td>
    </tr>
    <tr>   <!-- Row 3 -->
        <td style="border: 1px solid black">R3C1</td>
        <td style="border: 1px solid black">R3C2</td>
    </tr>
</table>

I’ve add a border to all four sides of each cell with the style attribute. Now we get:

Col 1 Heading Col 2 Heading
R2C1 R2C2
R3C1 R3C2

Hmm, not really what I want; it has literally drawn a box around each cell and left a small gap between them. My first thought when I saw this was that’s stupid — and I haven’t changed my mind.

Don’t worry, someone else obviously that it was stupid too, and added another declaration that can sort it out, it’s called border collapse and it can be applied to the whole table:

<table style="border-collapse: collapse">

This collapses the borders between cells into single border.

It looks like this:

Col 1 Heading Col 2 Heading
R2C1 R2C2
R3C1 R3C2

It looks how you would expect it to look; the property that puts it back how it was is border-collapse: separate.

  • Borders can be applied individually to any side of a cell using border-top, border-left &c. just like any block object.

So what else can we do, well we can change the cell size:

14.1.3

Specifying a column width

Let’s make the columns wider by setting a width property:

<table style=" border-collapse: collapse”>
    <tr>   <!-- Row 1 -->
        <th style="border: 1px solid black; width: 30%">Col 1 Heading</th>
        <th style="border: 1px solid black; width: 30%">Col 2 Heading</th>
    </tr>
    <tr>   <!-- Row 2 -->
        <td style="border: 1px solid black">R2C1</td>
        <td style="border: 1px solid black">R2C2</td>
    </tr>
    <tr>   <!-- Row 3 -->
        <td style="border: 1px solid black">R3C1</td>
        <td style="border: 1px solid black">R3C2</td>
    </tr>
</table>

This gives:

Col 1 Heading Col 2 Heading
R2C1 R2C2
R3C1 R3C2

Ok, I’ve set the width of the cells in the first row and this has change the width of all the cells below this. Why?

Well, a column of cells in a table is always set to the width of the widest cell in the column; you could see this in the previous examples where the cells in each row had been set to match the width of the cell with the widest text content (the text in the first row in all the previous cases).

In the above example, setting a cell width to 30% makes it wider than the content of any of the cells; it wouldn’t have mattered if I’d made the row 2 cells 30% wide, the thing would look exactly the same.

This is useful, it means we only have to set the cell width for a single cell in each column, all the other cells will automatically adopt the same setting. For clarity, I always set the cell width in the first row of a table.

And again, as with most things here, I occasionally break this rule.

14.1.4

Specifying a row height

Like column widths, the row defaults to the height of the highest cell within the row; in this example all the cells have text that is the same height, hence all the rows are the same height adding a height declaration to any cell within a row will make the whole row that height:

<table style="border-collapse:collapse; text-align: center">
    <tr>   <!-- Row 1 -->
        <th style="border: 1px solid black; width: 30%">Col 1 Heading</th>
        <th style="border: 1px solid black; width: 30%">Col 2 Heading</th>
    </tr>
    <tr>   <!-- Row 2 -->
        <td style="border: 1px solid black; height: 100px">R2C1</td>
        <td style="border: 1px solid black">R2C2</td>
    </tr>
    <tr>   <!-- Row 3 -->
        <td style="border: 1px solid black; height: 100px ">R3C1</td>
        <td style="border: 1px solid black">R3C2</td>
    </tr>
</table>

Col 1 Heading Col 2 Heading
R2C1 R2C2
R3C1 R3C2
  • The height declaration acts as a minimum height for the cell, if the cell contents require the cell to be higher than this, the cell will expand to meet the content.

14.1.5

Aligning text in a cell

The heading cells, by default, centre any text within them, data cells left justify text.

It is perfectly easy to align text to the left, right or centre of a cell with the standard CSS text-align property. The following centres all the text in every cell:

<table style="border-collapse:collapse; text-align: center">
    <tr>   <!-- Row 1 -->
        <th style="border: 1px solid black; width: 30%">Col 1 Heading</th>
        <th style="border: 1px solid black; width: 30%">Col 2 Heading</th>
    </tr>
    <tr>   <!-- Row 2 -->
        <td style="border: 1px solid black">R2C1</td>
        <td style="border: 1px solid black">R2C2</td>
    </tr>
    <tr>   <!-- Row 3 -->
        <td style="border: 1px solid black">R3C1</td>
        <td style="border: 1px solid black">R3C2</td>
    </tr>
</table>
  • I’ve removed the height styling from this example, row are just as high as they need to be.
Col 1 Heading Col 2 Heading
R2C1 R2C2
R3C1 R3C2

Individual cells can also have unique text alignments, here I’ve centred everything and then right justified the R3C3 cell:

<table style="border-collapse:collapse; text-align: center">
    <tr>   <!-- Row 1 -->
        <th style="border: 1px solid black; width: 30%">Col 1 Heading</th>
        <th style="border: 1px solid black; width: 30%">Col 2 Heading</th>
    </tr>
    <tr>   <!-- Row 2 -->
        <td style="border: 1px solid black">R2C1</td>
        <td style="border: 1px solid black">R2C2</td>
    </tr>
    <tr>   <!-- Row 3 -->
        <td style="border: 1px solid black">R3C1</td>
        <td style="border: 1px solid black; text-align: right">R3C2</td>
    </tr>
</table>

Giving:

Col 1 Heading Col 2 Heading
R2C1 R2C2
R3C1 R3C2

To align cells vertically, use vertical-align instead of text-align, the values for this property are (amongst others), top, bottom and middle.

14.1.6

Merging cells

Cells can be merged either horizontally or vertically (or both); by making a cell span either more than one column (horizontal merge) or more than one row (vertical merge).

Let’s start by merging horizontally, this uses the colspan property; let’s make the R3C1 cell span two columns:

<table style="border-collapse:collapse; text-align: center">
    <tr>   <!-- Row 1 -->
        <th style="border: 1px solid black; width: 30%">Col 1 Heading</th>
        <th style="border: 1px solid black; width: 30%">Col 2 Heading</th>
    </tr>
    <tr>   <!-- Row 2 -->
        <td style="border: 1px solid black">R2C1</td>
        <td style="border: 1px solid black">R2C2</td>
    </tr>
    <tr>   <!-- Row 3 -->
        <td colspan="2" style="border: 1px solid black">R3C1</td>
    </tr>
</table>

It looks like this:

Col 1 Heading Col 2 Heading
R2C1 R2C2
R3C1

I’ve had to do two things here:

First add the colspan="2" attribute to the cell I that I want to span more than one column.

Second I have to remove the cell or cells that are being overwritten, here I’ve added the colspan to the R3C1 cell <td> element as an attribute. I’ve also removed the R3C2 cell completely.

If I didn’t remove the R3C2 cell I would get this:

Col 1 Heading Col 2 Heading
R2C1 R2C2
R3C1 R3C2

Yeah, remember to delete the merged cell.

Go back to the original code (with four cells), this time we’ll merge R2C2 (almost a robot in a film) with R3C2.

This time it is a rowspan attribute:

<table style="border-collapse:collapse; text-align: center">
    <tr>   <!-- Row 1 -->
        <th style="border: 1px solid black; width: 30%">Col 1 Heading</th>
        <th style="border: 1px solid black; width: 30%">Col 2 Heading</th>
    </tr>
    <tr>   <!-- Row 2 -->
        <td style="border: 1px solid black">R2C1</td>
        <td rowspan="2" style="border: 1px solid black">R2C2</td>
    </tr>
    <tr>   <!-- Row 3 -->
        <td style="border: 1px solid black">R3C1</td>
    </tr>
</table>
Col 1 Heading Col 2 Heading
R2C1 R2C2
R3C1

Guess what happens:

Ok, I think that covers the basics, let’s look at the Practical Series tables.



End flourish image