HTML. Unit 6. Tables.

Introduction

This unit will allow you to get started with HTML tables, covering the very basics such as rows and cells, headings, making cells span multiple columns and rows, and how to group together specific cells for a better markup and also for styling purposes in the future (using CSS).

What is a table?

A table is a structured set of data made up of rows and columns (tabular data). A table allows you to quickly and easily look up values that indicate some kind of connection between different types of data, for example a person and their age, or a timetable, or the information for several countries, as shown in this example:

Countries Capitals Population Language
USA Washington, D.C. 309 million English
Sweden Stockholm 9 million Swedish

Tables are very commonly used in human society, and have been for a long time, as evidenced by this US Census document from 1800:

It is therefore no wonder that the creators of HTML provided a means by which to structure and present tabular data on the web.

How can you build a table?

Let’s dive into a practical example and build up a simple table like this one:

1. Hi, I’m your first cell. 2. I’m your second cell. 3. I’m your third cell. 4. I’m your fourth cell.
5. Second row, first cell. 6. Second row, second cell. 7. Second row, third cell. 8. Second row, fourth cell.

Let’s build the table step by step:

  1. The content of every table is enclosed by these two tags : <table> ... </table>. We will put everything inside these tags:
<table>
    1. Hi, I'm your first cell.
    2. I'm your second cell.
    3. I'm your third cell.
    4. I'm your fourth cell.
    5. Second row, first cell.
    6. Second row, second cell.
    7. Second row, third cell.
    8. Second row, fourth cell.
</table>
  1. The smallest container inside a table is a table cell, which is created by a <td> element (‘td’ stands for ‘table data’). We will put the contents of each cell inside these tags:
<table>
    <td>1. Hi, I'm your first cell.</td>
    <td>2. I'm your second cell.</td>
    <td>3. I'm your third cell.</td>
    <td>4. I'm your fourth cell.</td>
    <td>5. Second row, first cell.</td>
    <td>6. Second row, second cell.</td>
    <td>7. Second row, third cell.</td>
    <td>8. Second row, fourth cell.</td>
</table>
  1. As you will see, the cells are not placed underneath each other, rather they are automatically aligned with each other on the same row. Each <td> element creates a single cell and together they make up the first row, and every cell we add makes the row grow longer. To stop this row from growing and start placing subsequent cells on a second row, we need to use the <tr> element (‘tr’ stands for ‘table row’). Let’s do this now (we will wrap each row in an additional <tr> element, with each cell contained in a <td>):
<table>
    <!-- First row -->
    <tr>
        <td>1. Hi, I'm your first cell.</td>
        <td>2. I'm your second cell.</td>
        <td>3. I'm your third cell.</td>
        <td>4. I'm your fourth cell.</td>
    </tr>

    <!-- Second row -->
    <tr>
        <td>5. Second row, first cell.</td>
        <td>6. Second row, second cell.</td>
        <td>7. Second row, third cell.</td>
        <td>8. Second row, fourth cell.</td>
    </tr>
</table>

Important: Table borders

By default, the browser will not show the borders of the tables. To get the borders shown, we must use some CSS code. By now, we will insert the following code in the <head> section of each html document we create (we will learn the meaning of this code in another unit):

<head>
    ...
    <style>
        table {
            border-collapse: collapse;
        }
        table, th, td {
            border: 1px solid black;
        }
    </style>
</head>
<body>
    ...
</body>

Proposed exercise: Simple tables

Create a new web page, copy and paste a couple of times the table of the previous example and change the contents to get the following results:

Important: do not forget the small piece of CSS code inside the header section of your document to display the borders.
one two three four
five six seven eight
one two three
four five six
seven eight nine

Tables with images

You can insert any content you like inside the cells of a table. For example, images:

<table>
    <tr>
        <td><img src="https://picsum.photos/id/10/300/200" /></td>
        <td><img src="https://picsum.photos/id/1000/200/200" /></td>
    </tr>
    <tr>
        <td><img src="https://picsum.photos/id/1003/200/200" /></td>
        <td><img src="https://picsum.photos/id/1011/300/200" /></td>
    </tr>
</table>

Proposed exercise: Test pictures

Create a table of two columns and at least ten rows, and insert several pictures you like, as it is shown in the example above (with borders). Copy the same table in a new file and remove the CSS code to see the results now without borders.

You can use any image listed in “https://picsum.photos/images” . You only have to choose a picture and use the corresponding “id” and “size”. For example, “https://picsum.photos/id/1/200/200” is the image #1 (width=200px and height=200px) . Or “https://picsum.photos/id/103/300/200” is the image #103 (width=300px and height=200px).

Adding headers with <th> elements

Now let’s turn our attention to table headers. These are special cells that go at the start of a row or column and define the type of data that row or column contains. To illustrate why they are useful, have a look first at the following table:

Table without headers
Dog name Breed Age Eating Habits
Knocky Jack Russell 12 Eats everyone’s leftovers
Poppy Poodle 9 Nibbles at food
Buddy Streetdog 10 Hearty eater
Bailey Cocker Spaniel 5 Will eat till he explodes

The problem here is that, while you can kind of make out what’s going on, it is not as easy to cross reference data as it could be. If the column and row headings stood out in some way, it would be much better.

To recognize the table headers, both visually and semantically, you can use the <th> element (‘th’ stands for ‘table header’). This works in exactly the same way as a <td>, except that it denotes a header, not a normal cell. If we change all the <td> elements surrounding the table headers into <th> elements, the contents inside will be enhanced somehow by default. For example:

Table with headers
Dog name Breed Age Eating Habits
Knocky Jack Russell 12 Eats everyone’s leftovers
Poppy Poodle 9 Nibbles at food
Buddy Streetdog 10 Hearty eater
Bailey Cocker Spaniel 5 Will eat till he explodes

We will change the style of both <td> and <th> elements using CSS in future units. By now, let’s concentrate on the HTML code:

<table>
    <tr>
        <th>Dog name</th>
        <th>Breed</th>
        <th>Age</th>
        <th>Eating Habits</th>
    </tr>
    <tr>
        <th>Knocky</th>
        <td>Jack Russell</td>
        <td>12</td>
        <td>Eats everyone's leftovers</td>
    </tr>
    <tr>
        <th>Poppy</th>
        <td>Poodle</td>
        <td>9</td>
        <td>Nibbles at food</td>
    </tr>
    <tr>
        <th>Buddy</th>
        <td>Street dog</td>
        <td>10</td>
        <td>Hearty eater</td>
    </tr>
    <tr>
        <th>Bailey</th>
        <td>Cocker Spaniel</td>
        <td>5</td>
        <td>Will eat till he explodes</td>
    </tr>        
</table>

Proposed exercise: Dog walker

Create a web page with a table similar to the one in the previous example, to keep the information of all the clients of a dog walker. First you have to add three extra columns (to keep the name of the owners, their phone numbers, and the pictures of the dogs). After that you have to insert several rows to keep the data related to at least ten dogs.

In this case you can use another website to get test pictures about dogs: “https://placedog.net/images“. Open this URL and follow the instructions at the top of the page to insert each image. For example:
Dog name Owner Phone number Breed Age Eating Habits Picture
Knocky Fernando Ruiz 111222333 Jack Russell 12 Eats everyone’s leftovers
Poppy John Doe 222333444 Poodle 9 Nibbles at food
Buddy Peter Stark 333444555 Street dog 10 Hearty eater
Bailey Steve Doe 666777888 Cocker Spaniel 5 Will eat till he explodes

Adding a caption to your table with <caption>

You can give your table a caption by putting it inside a <caption> element and nesting that inside the <table> element. You should put it just below the opening <table> tag:

<table>
  <caption>Dinosaurs in the Jurassic period</caption>

  ...
</table>

As you can infer from the brief example above, the caption is meant to contain a description of the table contents. This is useful for all readers wishing to get a quick idea of whether the table is useful to them as they scan the page, but particularly for blind users. Rather than have a screenreader read out the contents of many cells just to find out what the table is about, he or she can rely on a caption and then decide whether or not to read the table in greater detail.

Proposed exercise: Simple table with caption and headers

Create a web page with a table similar to the one below, and insert some extra rows (at least ten).

Use a <caption> element to put the text “Simple table with headers”, and use the <th> element for the “First name” and “Last name” headers.
Simple table with headers
First name Last name
John Doe
Jane Doe

Proposed exercise: List of countries

Create a table with five columns and at least ten rows, and insert the data related to several countries. You can list for example the country names, their capitals, their population, the language, and several pictures, as done in the example at the beginning of the unit, but adding a new column to show an image. You have to use table headers (<th>) and caption (<caption>). Your table should look like shown below.

You can use again the website “https://picsum.photos/images” to get some random images for each country.
Countries I like
Countries Capitals Population Language Images
USA Washington, D.C. 309 million English
Sweden Stockholm 9 million Swedish

Row and column spanning

To provide additional control over how cells fit into (or span across) columns, both <th> and <td> support the colspan attribute, which lets you specify how many columns wide the cell should be, with the default being 1. Similarly, you can use the rowspan attribute on cells to indicate they should span more than one table row.

The following simple example shows a table listing people’s names along with various information about membership in a club or service. There are just four rows (including one header row), each with four columns (including one header column):

Name ID Member Since Balance
Margaret Nguyen 427311 0.00
Edvard Galinski 533175 37.00
Hoshi Nakamura 601942 15.00
<table>
  <tr>
    <th>Name</th>
    <th>ID</th>
    <th>Member Since</th>
    <th>Balance</th>
  </tr>
  <tr>
    <td>Margaret Nguyen</td>
    <td>427311</td>
    <td><time datetime="2010-06-03">June 3, 2010</time></td>
    <td>0.00</td>
  </tr>
  <tr>
    <td>Edvard Galinski</td>
    <td>533175</td>
    <td><time datetime="2011-01-13">January 13, 2011</time></td>
    <td>37.00</td>
  </tr>
  <tr>
    <td>Hoshi Nakamura</td>
    <td>601942</td>
    <td><time datetime="2012-07-23">July 23, 2012</time></td>
    <td>15.00</td>
  </tr>
</table>

Now, let’s introduce another column that shows the date the user’s membership ended, along with a super-heading above the “joined” and “canceled” dates called “Membership Dates”. This involves adding both row and column spans to the table, so that the heading cells can wind up in the right places. Let’s actually look at the output first:

Name ID Membership Dates Balance
Joined Canceled
Margaret Nguyen 427311 n/a 0.00
Edvard Galinski 533175 37.00
Hoshi Nakamura 601942 n/a 15.00

Notice how the heading area here is actually two rows, one with “Name”, “ID”, “Membership Dates”, and “Balance” headings, and the other with “Joined” and “Canceled”, which are sub-headings below “Membership Dates”. This is accomplished by:

  • Having the first row’s “Name”, “ID”, and “Balance” heading cells span two rows using the rowspan attribute, making them each be two rows tall.
  • Having the first row’s “Membership Dates” heading cell span two columns using the colspan attribute, which causes this heading to actually be two columns wide.
  • Having a second row of <th> elements that contains only the “Joined” and “Canceled” headings. Because the other columns are already occupied by first-row cells that span into the second row, these wind up correctly positioned under the “Membership Dates” heading.

The HTML is similar to the previous example’s, except for the addition of the new column in each data row, and the changes to the header. Those changes make the HTML look like this:

<table>
  <tr>
    <th rowspan="2">Name</th>
    <th rowspan="2">ID</th>
    <th colspan="2">Membership Dates</th>
    <th rowspan="2">Balance</th>
  </tr>
  <tr>
    <th>Joined</th>
    <th>Canceled</th>
  </tr>
  <tr>
    <th>Margaret Nguyen</td>
    <td>427311</td>
    <td><time datetime="2010-06-03">June 3, 2010</time></td>
    <td>n/a</td>
    <td>0.00</td>
  </tr>
  <tr>
    <th>Edvard Galinski</td>
    <td>533175</td>
    <td><time datetime="2011-01013">January 13, 2011</time></td>
    <td><time datetime="2017-04008">April 8, 2017</time></td>
    <td>37.00</td>
  </tr>
  <tr>
    <th>Hoshi Nakamura</td>
    <td>601942</td>
    <td><time datetime="2012-07-23">July 23, 2012</time></td>
    <td>n/a</td>
    <td>15.00</td>
  </tr>
</table>

The differences that matter here—for the purposes of discussing row and column spans—are in the first few lines of the code above. Note the use of rowspan to make the “Name”, “ID”, and “Balance” headers occupy two rows instead of just one, and the use of colspan to make the “Membership Dates” header cell span across two columns.

Proposed exercise: Your timetable

Create a web page to display your school timetable. You should create a table similar to the one below.

Notice that you have to use <th> elements for the headers, and colspan attribute for the BREAKS. You can also use the <strong> element to enhance the subject name in each cell.
Monday Tuesday Wednesday Thursday Friday
07:55h Computer Safety
Peter Williams
Computer Safety
Peter Williams
Computer Safety
Peter Williams
08:50h Network Services
Samuel Holland
Computer Safety
Peter Williams
Network Services
Samuel Holland
Computer Safety
Peter Williams
09:45h Network Operating Systems
Lucy Scott
Network Services
Samuel Holland
Web Applications
Fernando Ruiz
Network Services
Samuel Holland
Network Services
Samuel Holland
10:40h B R E A K
11:00h Network Operating Systems
Lucy Scott
Network Operating Systems
Lucy Scott
Business and Entrepreneurial Initiative
Rick Harris
Web Applications
Fernando Ruiz
Network Services
Samuel Holland
11:55h Business and Entrepreneurial Initiative
Rick Harris
Network Operating Systems
Lucy Scott
Network Operating Systems
Lucy Scott
Web Applications
Fernando Ruiz
Web Applications
Fernando Ruiz
12:50h B R E A K
13:10h Network Services
Samuel Holland
Business and Entrepreneurial Initiative
Rick Harris
Network Operating Systems
Lucy Scott
Network Operating Systems
Lucy Scott
Web Applications
Fernando Ruiz
14:05h Network Services
Samuel Holland
Web Applications
Fernando Ruiz
Network Operating Systems
Lucy Scott

Adding structure with <thead>, <tbody> and <tfoot>

As your tables get a bit more complex in structure, it is useful to give them more structural definition. One clear way to do this is by using <thead>, <tfoot> and <tbody>, which allow you to mark up a header, footer, and body section for the table. For example, we can improve the markup of any table by adding a simple header section like this:

The table header
First cell in the table body Second cell in the table body
<table>
    <thead>
        <tr>
            <th colspan="2">The table header</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>First cell in the table body</td>
            <td>Second cell in the table body</td>
        </tr>
    </tbody>
</table>

These new elements don’t make the table any more accessible to screen reader users, and don’t result in any visual enhancement on their own. They are however very useful for styling and layout, acting as useful hooks for adding CSS to your table. To give you some interesting examples, in the case of a long table you could make the table header and footer repeat on every printed page, and you could make the table body display on a single page and have the contents available by scrolling up and down.

To use all the elements together you just have to keep in mind the following considerations:

  • The <thead> element must wrap the part of the table that is the header (this is usually the first row containing the column headings, but this is not necessarily always the case).
  • The <tfoot> element needs to wrap the part of the table that is the footer (this might be a final row with items in the previous rows summed, for example). You can include the table footer right at the bottom of the table as you’d expect, or just below the table header (the browser will still render it at the bottom of the table).
  • The <tbody> element needs to wrap the other parts of the table content that aren’t in the table header or footer. It will appear below the table header or sometimes footer, depending on how you decided to structure it in the future.

The <thead> +<tbody> elements

Let’s add for example the <thead> and <tbody> sections to the table of the members of a club:

Name ID Membership Dates Balance
Joined Canceled
Margaret Nguyen 427311 n/a 0.00
Edvard Galinski 533175 37.00
Hoshi Nakamura 601942 n/a 15.00
<table>
  <thead>
    <tr>
      <th rowspan="2">Name</th>
      <th rowspan="2">ID</th>
      <th colspan="2">Membership Dates</th>
      <th rowspan="2">Balance</th>
    </tr>
    <tr>
      <th>Joined</th>
      <th>Canceled</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">Margaret Nguyen</td>
      <td>427311</td>
      <td><time datetime="2010-06-03">June 3, 2010</time></td>
      <td>n/a</td>
      <td>0.00</td>
    </tr>
    <tr>
      <th scope="row">Edvard Galinski</td>
      <td>533175</td>
      <td><time datetime="2011-01013">January 13, 2011</time></td>
      <td><time datetime="2017-04008">April 8, 2017</time></td>
      <td>37.00</td>
    </tr>
    <tr>
      <th scope="row">Hoshi Nakamura</td>
      <td>601942</td>
      <td><time datetime="2012-07-23">July 23, 2012</time></td>
      <td>n/a</td>
      <td>15.00</td>
    </tr>
  </tbody>
</table>

Proposed exercise: Club members

Create a web page to keep a listing of the members of a club as we have done in the example above. You can use the same source code as explained before, but you have to add a couple of columns: one to write the email address of each member, and the other to show their portraits (pictures). You also have to add several rows to the table so that it contains at least ten club members (you can use random names, dates and balances you make up on your own).

The <thead> +<tbody> +<tfoot> elements

Let’s put all these new elements into action with another table, where we will use all possible sections (<thead>, <tbody> and <tfoot>). Have a look at the following example:

How I chose to spend my money
Purchase Location Date Evaluation Cost (€)
Haircut Hairdresser 12/20 Great idea 30
Lasagna Restaurant 12/20 Regrets 18
Shoes Shoeshop 13/20 Big regrets 65
Toothpaste Supermarket 13/20 Good 5
SUM 118

We must put the obvious headers row inside a <thead> element, the “SUM” row inside a <tfoot> element, and the rest of the content inside a <tbody> element. You’ll see that adding the <tfoot> element has caused the “SUM” row to go down to the bottom of the table. And finally we will add a colspan attribute to make the “SUM” cell span across the first four columns, so the actual number appears at the bottom of the “Cost” column:

<table>
    <caption>How I chose to spend my money</caption>
    <thead>
        <tr>
            <th>Purchase</th>
            <th>Location</th>
            <th>Date</th>
            <th>Evaluation</th>
            <th>Cost (€)</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Haircut</td>
            <td>Hairdresser</td>
            <td>12/20</td>
            <td>Great idea</td>
            <td>30</td>
        </tr>
        <tr>
            <td>Lasagna</td>
            <td>Restaurant</td>
            <td>12/20</td>
            <td>Regrets</td>
            <td>18</td>
        </tr>
        <tr>
            <td>Shoes</td>
            <td>Shoeshop</td>
            <td>13/20</td>
            <td>Big regrets</td>
            <td>65</td>
        </tr>
        <tr>
            <td>Toothpaste</td>
            <td>Supermarket</td>
            <td>13/20</td>
            <td>Good</td>
            <td>5</td>
        </tr>
    </tbody>
    <tfoot>
        <tr>
            <td colspan="4">SUM</td>
            <td>118</td>
        </tr>
    </tfoot>
</table>

Proposed exercise: How to spend your money

Create a web page to write down how would you spend your money. You can use the same source code as the previous example, but you have to perform the following changes: add several rows with any things you would like to do (at least ten rows), and finally in the “Location” column you must use images instead of text.

You can use again the site “https://picsum.photos/images” to get the pictures of your preferred locations.

Proposed exercise: Captions

Add captions to those tables you have created previously and don’t have any caption yet. Do not forget to validate your code again.

Proposed exercise: Table structure

Add header, footer and body sections to all the tables you have created in previous exercises and don’t have those sections yet. Do not forget to validate your code again.

Quiz

Test your skills with this quiz about tables and some other concepts related to this unit.