Tables Tutorial

HTML tables are particularly good at creating a logical, and heirarchical structure for complex data sets. A lot of the simple tables you see on the web, such as ecommerce pricing and feature tables, aren’t actually tables. Instead, simple tables are usually created with unordered lists containing column data and CSS to position the lists next to each other. If you want to create these sorts of tables, take a look at our list and CSS tutorials.

On the other hand, if you have a complex data set that needs strong logical structure, HTML tables are the right tool for the job.

Table Building Blocks

Tables are created by nesting a variety of elements between table tags. Tables are organized into rows, not columns, by the table row (tr) element. Each table row is made up of one or more table data (td)entries. Columns are formed automatically when table data elements from each subsequent table row automatically line up in vertical columns.

Those three elements, table, tr, and td, are the basic building blocks of HTML tables. Here’s an example of how we can use just those three elements to create a simple table:

<table>
<tr>
    <td>Mood</td>
    <td>Color</td>
    <td>Weather</td>
</tr>
<tr>
    <td>Happy</td>
    <td>Yellow</td>
    <td>Sunny</td>
</tr>
<tr>
    <td>Sleepy</td>
    <td>Gray</td>
    <td>Cloudy</td>
</tr>
</table>

And here’s how our simple table will show up in the browser:

Mood Color Weather
Happy Yellow Sunny
Sleepy Gray Cloudy

Adding Structure to a Table

There are additional elements we can use to add semantic meaning to the data in our table. The most important element, and one that should be present for every table, is the table heading th tag. This tag is used in place of the td tags in the first row to identify entries that should be used as column headings.

There are other elements which may optionally be used to group table heading, body, and footer elements together. These elements are not required for HTML tables, but if you are working with a very large data set, it’s a good idea to include them.

  • thead is the table header container, and it is used to contain the entries with the th tag.
  • tfoot is the table footer container. If your data set contains a final summary or disclaimer row, wrap it in tfoot tags and place it immediately after the thead container. Even though the tfoot element appears above the tbody element, when rendered in the browser it will automatically appear at the bottom of the table.
  • The tbody element is used to contain all of the data that should appear between the header and the footer.

In order to implement these structure-adding elements, we need some data to work with.

Image showing the most popular content management systems.

This image comes from W3Techs, and was captured Feb 2, 2016.

Let’s convert the data in this image into a table making use of all the elements we’ve covered so far.

<table>
    <thead>
        <tr>
            <th>CMS</th>
            <th>Usage</th>
            <th>Change Since Jan 1</th>
            <th>Market Share</th>
            <th>Change Since Jan 1</th>
        </tr>
    </thead>
    <tfoot>
        <tr>
            <td>Totals</td>
            <td>33.3%</td>
            <td></td>
            <td>76%</td>
            <td></td>
        </tr>
    </tfoot>
    <tbody>
        <tr>
            <td>WordPress</td>
            <td>25.8%</td>
            <td>+0.2%</td>
            <td>59.1%</td>
            <td>+0.3%</td>
        </tr>
        <tr>
            <td>Joomla</td>
            <td>2.8%</td>
            <td>No Change</td>
            <td>6.4%</td>
            <td>No Change</td>
        </tr>
        <tr>
            <td>Drupal</td>
            <td>2.2%</td>
            <td>+0.1%</td>
            <td>4.9%</td>
            <td>No Change</td>
        </tr>
        <tr>
            <td>Magento</td>
            <td>1.3%</td>
            <td>+0.1%</td>
            <td>2.9%</td>
            <td>No Change</td>
        </tr>
        <tr>
            <td>Blogger</td>
            <td>1.2%</td>
            <td>No Change</td>
            <td>2.7%</td>
            <td>No Change</td>
        </tr>
    </tbody>
</table>

Note that we need to create empty td elements for every box that should appear empty. Let’s see how our table looks so far:

CMS Usage Change Since Jan 1 Market Share Change Since Jan 1
Totals 33.3% 76%
WordPress 25.8% +0.2% 59.1% +0.3%
Joomla 2.8% No Change 6.4% No Change
Drupal 2.2% +0.1% 4.9% No Change
Magento 1.3% +0.1% 2.9% No Change
Blogger 1.2% No Change 2.7% No Change

Adding a Caption

We want our website visitors to understand the data in our table. One thing our table lacks is a caption that describes the contents of the table. We could use a heading element, such as an h2, and that might be the easiest solution. However, visitors using assistive technologies will gain the most benefit if we use the caption element and add it to the table itself.

We can add a caption by wrapping it in caption tags and placing it at the very top of our table element.

<table>
    <caption>Most Popular Content Management Systems</caption>
    <thead>
<!--Followed by the rest of the table data-->

Adding a Footer Comment

Since our data comes from an external source, we really do need to add some sort of attribution to our table. Let’s do that by adding a comment in a row at the bottom of the table explaining the context surrounding our data and giving proper attribution. We should also explain what the Usage and Market Share columns mean to keep our website visitors from having to guess at the meaning of that data.

We want our footer explanation and attribution rows to span the full width of the table – a total width of five columns. To do this we need to use the colspan attribute.

<!--Preceded by Table Heading Content-->
</thead>
<tfoot>
    <tr>
        <td>Totals</td>
        <td>33.3%</td>
        <td></td>
        <td>76%</td>
        <td></td>
    </tr>
    <tr>
        <td colspan="5">* <strong>Usage</strong> is percentage of surveyed websites 
        that use the associated CMS. <strong>Market Share</strong> is the percentage 
        of surveyed websites powered by a CMS that use the associated CMS. For example, 
        25.8% of all surveyed websites use WordPress, and WordPress commands 59.1% 
        market share of the total CMS market.</td>
    </tr>
    <tr>
        <td colspan="5">The data in this table is provided courtesy of 
        <a href="http://w3techs.com" target="_blank">W3Techs</a> and was captured in 
        February 2016. To learn more about this topic visit the 
        <a href="http://w3techs.com/technologies/overview/content_management/all" 
        target="_blank">overview of content management systems</a> from W3Techs.</td>
    </tr>
</tfoot>
<tbody>
<!--Followed by Table Body Content-->

Combining Duplicated Content

We can clean up our table a little by collapsing duplicated content. For example, in the far left-hand column we have the value “No Change” expressed four times in a row. We could collapse all four values into a single cell using the rowspan attribute.

To use rowspan we need to identify the first td element that we want to include in the block of grouped cells. Here it is:

<!--Preceded by WordPress Row-->
        <tr>
            <td>Joomla</td>
            <td>2.8%</td>
            <td>No Change</td>
            <td>6.4%</td>
            <td rowspan="4">No Change</td>
        </tr>
<!--Followed by Drupal Row-->

We do need to take one other step when we add this modification to our table HTML and that is to delete the final td element from each of the data sets that we want to collapse into our rowspan. If we don’t don’t delete the td elements that are to be collapsed into the rowspan they will remain in the flow of the table, but appear next to the rowspan floating on the right-hand side of the table.

Pulling it All Together

Let’s pull together our table code, the caption, the footer comment, and the column-collapsing code into one cohesive code block.

<table>
    <caption>Most Popular Content Management Systems</caption>
    <thead>
        <tr>
            <th>CMS</th>
            <th>Usage *</th>
            <th>Change Since Jan 1</th>
            <th>Market Share *</th>
            <th>Change Since Jan 1</th>
        </tr>
    </thead>
    <tfoot>
        <tr>
            <td>Totals</td>
            <td>33.3%</td>
            <td></td>
            <td>76%</td>
            <td></td>
        </tr>
        <tr>
            <td colspan="5">* <strong>Usage</strong> is percentage of surveyed websites 
            that use the associated CMS. <strong>Market Share</strong> is the percentage 
            of surveyed websites powered by a CMS that use the associated CMS. For example, 
            25.8% of all surveyed websites use WordPress, and WordPress commands 59.1% 
            market share of the total CMS market.</td>
        </tr>
        <tr>
            <td colspan="5">The data in this table is provided courtesy of 
            <a href="http://w3techs.com" target="_blank">W3Techs</a> and was captured in 
            February 2016. To learn more about this topic visit the 
            <a href="http://w3techs.com/technologies/overview/content_management/all" 
            target="_blank">overview of content management systems</a> from W3Techs.</td>
        </tr>
    </tfoot>
    <tbody>
        <tr>
            <td>WordPress</td>
            <td>25.8%</td>
            <td>+0.2%</td>
            <td>59.1%</td>
            <td>+0.3%</td>
        </tr>
        <tr>
            <td>Joomla</td>
            <td>2.8%</td>
            <td>No Change</td>
            <td>6.4%</td>
            <td rowspan="4">No Change</td>
        </tr>
        <tr>
            <td>Drupal</td>
            <td>2.2%</td>
            <td>+0.1%</td>
            <td>4.9%</td>
            <!--Deleted td element to be collapsed with rowspan-->
        </tr>
        <tr>
            <td>Magento</td>
            <td>1.3%</td>
            <td>+0.1%</td>
            <td>2.9%</td>
            <!--Deleted td element to be collapsed with rowspan-->
        </tr>
        <tr>
            <td>Blogger</td>
            <td>1.2%</td>
            <td>No Change</td>
            <td>2.7%</td>
            <!--Deleted td element to be collapsed with rowspan-->
        </tr>
    </tbody>
</table>

With all of our changes pulled together, here’s how our table looks.

Most Popular Content Management Systems
CMS Usage * Change Since Jan 1 Market Share * Change Since Jan 1
Totals 33.3% 76%
* Usage is percentage of surveyed websites that use the associated CMS. Market Share is the percentage of surveyed websites powered by a CMS that use the associated CMS. For example, 25.8% of all surveyed websites use WordPress, and WordPress commands 59.1% market share of the total CMS market.
The data in this table is provided courtesy of W3Techs and was captured in February 2016. To learn more about this topic visit the overview of content management systems from W3Techs.
WordPress 25.8% +0.2% 59.1% +0.3%
Joomla 2.8% No Change 6.4% No Change
Drupal 2.2% +0.1% 4.9%
Magento 1.3% +0.1% 2.9%
Blogger 1.2% No Change 2.7%

Conclusion & Next Steps

Tables give us a way to present data sets in a way that is easy to digest. Table syntax in HTML is fairly straightforward and with just a little practice you’ll be creating complex tables with ease.

Our table looks pretty good right now, and includes all of the critical elements. However, there’s a lot more we can do with this table.

  • The table caption could use some styling to add emphasis and draw attention.
  • The explanation and attribution comments in the table footer need to shrink in size a bit so that they aren’t quite as prevalent.
  • The first column entries could be styled differently from the rest of the table data cells.
  • The empty cells in the “Totals” row could use some styling to make it clear that they are supposed to be empty.
  • We may want to center the contents in our td elements.
  • This table doesn’t look very good on a small screen. We should make some changes so that it renders a little better on smaller devices.

We’ll implement all of these changes in our next tutorial: Styling Tables.