8. Table Markup

8

Table Markup

In this Chapter

How tables are used

Basic table structure

Spanning rows and columns

Row and column groups

Making tables accessible

Before we launch into the markup for tables, let’s check in with our progress so far. We’ve covered a lot of territory: how to establish the basic structure of an HTML document, how to mark up text to give it meaning and structure, how to make links, and how to embed simple images on the page.

This chapter and the next two chapters, Chapter 9, Forms, and Chapter 10, Embedded Media, describe the markup for specialized content that you might not have a need for right away. If you’re getting antsy to make your pages look good, skip right to Part III and start playing with Cascading Style Sheets. The tables, forms, and media chapters will be here when you’re ready for them.

Are you still with me? Great. Let’s talk tables. We’ll start out by reviewing how tables should be used, then learn the elements used to create them. Remember, this is an HTML chapter, so we’re going to focus on the markup that structures the content into tables, and we won’t be concerned with how the tables look (that will be tackled in various CSS chapters in Part III).

How to Use Tables

HTML tables were created for instances when you need to add tabular material (data arranged into rows and columns) to a web page. Tables may be used to organize schedules, product comparisons, statistics, or other types of information, as shown in Figure 8-1. Note that “data” doesn’t necessarily mean numbers. A table cell may contain any sort of information, including numbers, text elements, and even images and multimedia objects.

Figure 8-1. Examples of tables used for tabular information, such as charts, calendars, and schedules.

In visual browsers, the arrangement of data in rows and columns gives readers an instant understanding of the relationships between data cells and their respective header labels. Bear in mind when you are creating tables, however, that some readers will be hearing your data read aloud with a screen reader or reading Braille output. Later in this chapter, we’ll discuss measures you can take to make table content accessible to users who don’t have the benefit of visual presentation.

In the days before style sheets, tables were the only option for creating multicolumn layouts or controlling alignment and whitespace. Layout tables, particularly the complex nested table arrangements that were once standard web design fare, have gone the way of the dodo. If you need rows and columns for presentation purposes, there are alternatives that use CSS to achieve the desired effect. In one approach known as CSS Tables, nested divs provide the markup, and CSS Table properties make them behave like rows and cells in the browser. You can also achieve many of the effects that previously required table markup using Flexbox and Grid Layout techniques (see Chapter 16, CSS Layout with Flexbox and Grid).

That said, this chapter focuses on HTML table elements used to semantically mark up rows and columns of data as described in the HTML specification.

Minimal Table Structure

<table>…</table>

Tabular content (rows and columns)

<tr>…</tr>

Table row

<th>…</th>

Table header

<td>…</td>

Table cell data

Let’s take a look at a simple table to see what it’s made of. Here is a small table with three rows and three columns that lists nutritional information.

Menu item

Calories

Fat (g)

Chicken noodle soup

120

2

Caesar salad

400

26

Figure 8-2 reveals the structure of this table according to the HTML table model. All of the table’s content goes into cells that are arranged into rows. Cells contain either header information (titles for the columns, such as “Calories”) or data, which may be any sort of content.

Figure 8-2. Tables are made up of rows that contain cells. Cells are the containers for content.

Simple enough, right? Now let’s look at how those parts translate into elements (Figure 8-3).

Figure 8-3. The elements that make up the basic structure of a table.

Figure 8-3 shows the elements that identify the table (table), rows (tr, for “table row”), and cells (th, for “table headers,” and td, for “table data”). Cells are the heart of the table, because that’s where the actual content goes. The other elements just hold things together.

What we don’t see are column elements. The number of columns in a table is implied by the number of cells in each row. This is one of the things that make HTML tables potentially tricky. Rows are easy—if you want the table to have three rows, just use three tr elements. Columns are different. For a table with four columns, you need to make sure that every row has four td or th elements. (There’s more to the column story, which I cover in the section “Row and Column Groups” later in this chapter.)

Written out in a source document, the markup for the table in Figure 8-3 looks like the following sample. It is common to stack the th and td elements in order to make them easier to find in the source. This does not affect how the browser renders them.

<table>   <tr>      <th>Menu item</th>      <th>Calories</th>      <th>Fat (g)</th>   </tr>   <tr>      <td>Chicken noodle soup</td>      <td>120</td>      <td>2</td>   </tr>   <tr>      <td>Caesar salad</td>      <td>400</td>      <td>26</td>   </tr></table>

Remember, all the content must go in cells—that is, within td or th elements. You can put any content in a cell: text, a graphic, or even another table.

Start and end table tags identify the beginning and end of the tabular material. The table element may directly contain only some number of tr (row) elements, a caption and, optionally, the row and column group elements listed in the “Row and Column Groups” section. The only thing that can go in the tr element is some number of td or th elements. In other words, there may be no text content within the table and tr elements that isn’t contained within a td or th.

Finally, Figure 8-4 shows how the table would look in a simple web page, as displayed by default in a browser. I know it’s not exciting. Excitement happens in the CSS. What is worth noting is that tables always start on new lines by default in browsers.

Figure 8-4. The default rendering of our sample table in a browser.

Here is the source for another table. Can you tell how many rows and columns it will have when it is displayed in a browser?

<table>   <tr>      <th>Burgers</th>      <td>Organic Grass-fed Beef</td>      <td>Black Bean Veggie</td>   </tr>   <tr>      <th>Fries</th>      <td>Hand-cut Idaho potato</td>      <td>Seasoned sweet potato</td>   </tr></table>

If you guessed that it’s a table with two rows and three columns, you are correct! Two tr elements create two rows; one th and two td elements in each row create three columns.

Table Headers

As you can see in Figure 8-4, the text marked up as headers (th elements) is displayed differently from the other cells in the table (td elements). The difference, however, is not purely cosmetic. Table headers are important because they provide information or context about the cells in the row or column they precede. The th element may be handled differently than tds by alternative browsing devices. For example, screen readers may read the header aloud before each data cell (“Menu item: Caesar salad, Calories: 400, Fat-g: 26”).

In this way, headers are a key tool for making table content accessible. Don’t try to fake them by formatting a row of td elements differently than the rest of the table. Conversely, don’t avoid using th elements because of their default rendering (bold and centered). Instead, mark up the headers semantically and change the presentation later with a style rule.

That covers the basics. Before we get fancier, try your hand at Exercise 8-1.

Exercise 8-1. Making a simple table

Try writing the markup for the table shown in Figure 8-5. You can open a text editor or just write it down on paper. The finished markup is provided in the materials folder ().

Note that I’ve added a 1-pixel border around cells with a style rule just to make the structure clear. If you would like borders on your tables, copy this style element into the head of the document(s) you create for the exercises in this chapter:

<style> td, th {  border: 1px solid gray;} </style>

Be sure to close all table elements. Technically, you are not required to close tr, th, and td elements, but I want you to get in the habit of writing tidy source code for maximum predictability across all browsing devices.

Figure 8-5. Write the markup for this table.

Spanning Cells

One fundamental feature of table structure is cell spanning, which is the stretching of a cell to cover several rows or columns. Spanning cells allows you to create complex table structures, but it has the side effect of making the markup a little more difficult to keep track of. It can also make it potentially more difficult for users with screen readers to follow.

You make a header or data cell span by adding the colspan or rowspan attributes, as we’ll discuss next.

Column Spans

Column spans, created with the colspan attribute in the td or th element, stretch a cell to the right to span over the subsequent columns (Figure 8-6). Here a column span is used to make a header apply to two columns (I’ve added a border around the cells to reveal the structure of the table in the screenshot).

<table>   <tr>      <th colspan="2">Fat</th>   </tr>   <tr>      <td>Saturated Fat (g)</td>      <td>Unsaturated Fat (g)</td>   </tr></table>

Figure 8-6. The colspan attribute stretches a cell to the right to span the specified number of columns.

Notice in the first row (tr) that there is only one th element, while the second row has two td elements. The th for the column that was spanned over is no longer in the source; the cell with the colspan stands in for it. Every row should have the same number of cells or equivalent colspan values. For example, there are two td elements and the colspan value is 2, so the implied number of columns in each row is equal.

WARNING

Be careful with colspan values. If you specify a number that exceeds the number of columns in the table, browsers add columns to the existing table, which typically screws things up.

Try your hand at column spanning in Exercise 8-2.

exercise 8-2. Column spans

Try writing the markup for the table shown in Figure 8-7. You can open a text editor or just write it down on paper. I’ve added borders to reveal the cell structure in the figure, but your table won’t have them unless you add the style sheet shown in Exercise 8-1. Again, the final markup is provided in the materials folder.

Some hints:

  • The first and third rows show that the table has a total of three columns.
  • When a cell is spanned over, its td element does not appear in the table.

Figure 8-7. Practice column spans by writing the markup for this table.

Row Spans

Row spans, created with the rowspan attribute, work just like column spans, but they cause the cell to span downward over several rows. In this example, the first cell in the table spans down three rows (Figure 8-8).

<table>   <tr>      <th rowspan="3">Serving Size</th>      <td>Small (8oz.)</td>   </tr>    <tr>      <td>Medium (16oz.)</td>   </tr>   <tr>      <td>Large (24oz.)</td>   </tr></table>

Again, notice that the td elements for the cells that were spanned over (the first cells in the remaining rows) do not appear in the source. The rowspan="3" implies cells for the subsequent two rows, so no td elements are needed.

If you loved spanning columns, you’ll love spanning rows in Exercise 8-3.

Figure 8-8. The rowspan attribute stretches a cell downward to span the specified number of rows.
exercise 8-3. Row spans

Try writing the markup for the table shown in Figure 8-9. Remember that cells that are spanned over do not appear in the table code.

Some hints:

  • Rows always span downward, so the “oranges” cell is part of the first row even though its content is vertically centered.
  • Cells that are spanned over do not appear in the code.

Figure 8-9. Practice row spans by writing the markup for this table.

Space in and Between Cells

By default, tables expand just enough to fit the content of the cells, which can look a little cramped. Old versions of HTML included cellpadding and cellspacing attributes for adding space within and between cells, but they have been kicked out of HTML5 as they are obsolete, presentational markup.The proper way to adjust table cell spacing is with style sheets, of course. The “Styling Tables” section in Chapter 19, More CSS Techniques addresses cell spacing.

Table Accessibility

As a web designer, it is important that you always keep in mind how your site’s content is going to be used by visitors with impaired sight. It is especially challenging to make sense of tabular material by using a screen reader, but the HTML specification provides measures to improve the experience and make your content more understandable.

Describing Table Content

<caption>…</caption>

Title or description to be displayed with the table

The most effective way to give sight-impaired users an overview of your table is to give it a title or description with the caption element. Captions display next to the table (generally, above it) and can be used to describe the table’s contents or provide hints on how it is structured.

When used, the caption element must be the first thing within the table element, as shown in this example, which adds a caption to the nutritional chart from earlier in the chapter:

<table>   <caption>Nutritional Information</caption>   <tr>      <th>Menu item</th>      <th>Calories</th>      <th>Fat (g)</th>   </tr>   <!-- table continues --></table>

The caption is displayed above the table by default, as shown in Figure 8-10, although you can use a style sheet property to move it below the table (caption-side: bottom).

Figure 8-10. The table caption is displayed above the table by default.

For longer descriptions, you could consider putting the table in a figure element and using the figcaption element for the description. The HTML5 specification has a number of suggestions for providing table descriptions ().

Connecting Cells and Headers

We discussed headers briefly as a straightforward method for improving the accessibility of table content, but sometimes it may be difficult to know which header applies to which cells. For example, headers may be at the left or right edge of a row rather than at the top of a column. And although it may be easy for sighted users to understand a table structure at a glance, for users hearing the data as text, the overall organization is not as clear. The scope and headers attributes allow authors to explicitly associate headers and their respective content.

scope

The scope attribute associates a table header with the row, column, group of rows (such as tbody), or column group in which it appears by using the values row, col, rowgroup, or colgroup, respectively. This example uses the scope attribute to declare that a header cell applies to the current row:

<tr>   <th scope="row">Mars</th>   <td>.95</td>   <td>.62</td>   <td>0</td></tr>

Accessibility experts recommend that every th element contain a scope attribute to make its associated data explicitly clear.

headers

For really complicated tables in which scope is not sufficient to associate a table data cell with its respective header (such as when the table contains multiple spanned cells), the headers attribute is used in the td element to explicitly tie it to a header’s id value. In this example, the cell content “.38” is tied to the header “Diameter measured in earths”:

<th id="diameter">Diameter measured in earths</th><!-- many other cells --><td headers="diameter">.38</td><!-- many other cells -->

Unfortunately, support of the id/headers feature is unreliable. The recommended best practice is to create tables in a way that a simple scope attribute will do the job.

Browser support alert

Although the advanced table features intended to improve accessibility have been in the specs for many years, support by screen readers and other assistive devices is unreliable at best. It is still recommended that you mark up your data semantically within table cells and that they make sense when read in order from the source, which is exactly how some of your visitors may encounter them.

This section is obviously only the tip of the iceberg of table accessibility. In-depth instruction on authoring accessible tables is beyond the scope of this beginner book. If you’d like to learn more, I recommend “Creating Accessible Tables” at WebAIM () as an excellent starting point.

There is one more important set of elements for helping make the semantic structure of a table clear: row and column grouping elements.

Row and Column Groups

The sample tables we’ve been looking at so far in this chapter have been stripped down to their bare essentials to make the structure clear while you’re learning how tables work. But tables in the real world are not always so simple. Check out the beauty in Figure 8-11 from the CSS Writing Modes Level 3 spec. You can identify three groups of columns (one with headers, two with two columns each), and three groupings of rows (headers, data, and a footnote).

Conceptual table groupings like these are marked up with row group and column group elements that provide additional semantic structure and more “hooks” for styling or scripting. For example, the row and column groups in Figure 8-11 were styled with thicker borders to make them stand out visually.

Figure 8-11. An example of a table with row and column groups (from the CSS Writing Modes Level 3 specification).

Row Group Elements

<thead>…</thead>

Table header row group

<tbody>…</tbody>

Table body row group

<tfoot>…</tfoot>

Table footer row group

You can describe rows or groups of rows as belonging to a header, footer, or the body of a table by using the thead, tfoot, and tbody elements, respectively. Some user agents (another word for a browser) may repeat the header and footer rows on tables that span multiple pages. For example, the head and foot rows may print on every page of a multipage table. Authors may also use these elements to apply styles to various regions of a table.

Row group elements may only contain one or more tr elements. They contain no direct text content. The thead element should appear first, followed by any number of tbody elements, followed by an optional tfoot.

This is the row group markup for the table in Figure 8-11 (td and th elements are hidden to save space):

<table>…<thead>  <!-- headers in these rows-->
  <tr></tr>   <tr></tr>  <tr></tr><thead><tbody>  <!-- data -->
  <tr></tr>  <tr></tr>  <tr></tr>  <tr></tr>  <tr></tr>  <tr></tr></tbody><tfoot>  <!-- footnote -->
  <tr></tr></tfoot></table>

Column Group Elements

<colgroup>…</colgroup>

A semantically related group of columns

<col>…</col>

One column in a column group

As you’ve learned, columns are implied by the number of cells (td or th) in each row. You can semantically group columns (and assign id and class values) using the colgroup element.

Column groups are identified at the start of the table, just after the caption if there is one, and they give the browser a little heads-up as to the column arrangement in the table. The number of columns a colgroup represents is specified with the span attribute. Here is the column group section at the beginning of the table in Figure 8-11:

<table>  <caption>…</caption>  <colgroup></colgroup>  <colgroup span="2"></colgroup>  <colgroup span="2"></colgroup><!-- rest of table... -->

That’s all there is to it. If you need to access individual columns within a colgroup for scripting or styling, identify them with col elements. The previous column group section could also have been written like this:

  <colgroup></colgroup>  <colgroup>    <col class="start">    <col class="end">  </colgroup>  <colgroup>    <col class="start">    <col class="end">  </colgroup>

Note that the colgroup elements contain no content—they only provide an indication of semantically relevant column structure. The empty col elements are used as handles for scripts or styles, but are not required.

Note

When colgroup elements contain col elements, they must not have a span attribute.

Wrapping Up Tables

This chapter gave you a good overview of the components of HTML tables. Exercise 8-4 combines most of what we’ve covered to give you a little more practice at authoring tables.

exercise 8-4. The table challenge

Now it’s time to put together the table writing skills you’ve acquired in this chapter. Your challenge is to write out the source document for the table shown in Figure 8-12.

Figure 8-12. The table challenge.

I’ll walk you through it one step at a time.

  1. First, open a new document in your text editor and set up its overall structure (DOCTYPE, html, head, title, and body elements). Save the document as table.html in the directory of your choice.
  2. Next, in order to make the boundaries of the cells and table clear when you check your work, I’m going to have you add some simple style sheet rules to the document. Don’t worry about understanding exactly what’s happening here (although it’s fairly intuitive); just insert this style element in the head of the document exactly as you see it here:
    <head>  <title>Table Challenge</title>  <style>    td, th { border: 1px solid #CCC; }    table { border: 1px solid black; }  </style></head>
  3. Now it’s time to start building the table. I usually start by setting up the table and adding as many empty row elements as I’ll need for the final table as placeholders, as shown here. You can tell from the figure that there are five rows in this table:
    <body>  <table>    <tr></tr>    <tr></tr>    <tr></tr>    <tr></tr>    <tr></tr>  </table></body>
  4. Start with the top row, and fill in the th and td elements from left to right, including any row or column spans as necessary. I’ll help with the first row.

    The first cell (the one in the top-left corner) spans down the height of two rows, so it gets a rowspan attribute. I’ll use a th here to keep it consistent with the rest of the row. This cell has no content:

    <table>  <tr>    <th rowspan="2"></th>  </tr>

    The cell in the second column of the first row spans over the width of two columns, so it gets a colspan attribute:

    <table>  <tr>    <th rowspan="2"></th>    <th colspan="2">A common header for two subheads</th>  </tr>

    The cell in the third column has been spanned over by the colspan we just added, so we don’t need to include it in the markup. The cell in the fourth column also spans down two rows:

    <table>  <tr>    <th rowspan="2"></th>    <th colspan="2">A common header for two subheads</th>    <th rowspan="2">Header 3</th>  </tr>
  5. Now it’s your turn. Continue filling in the th and td elements for the remaining four rows of the table. Here’s a hint: the first and last cells in the second row have been spanned over. Also, if it’s bold in the example, make it a header.
  6. To complete the content, add the title over the table by using the caption element.
  7. Use the scope attribute to make sure that the Thing A, Thing B, and Thing C headers are associated with their respective rows.
  8. Finally, give the table row and column groups for greater sematic clarity. There is no tfoot in this table. There are two column groups: one column for headers, the rest for data. Use the span attribute (no need for individual column identification).
  9. Save your work and open the file in a browser. The table should look just like the one on this page. If not, go back and adjust your markup. If you’re stumped, the final markup for this exercise is provided in the materials folder.

Test Yourself

The answers to these questions appear in Appendix A.

  1. What are the parts (elements) of a basic HTML table?
  2. What elements can a table contain directly (i.e., first-level children)?
  3. What elements can a tr contain?
  4. When would you use the col (column) element?
  5. Find five errors in this table markup:
    <caption>Primetime Television 1965</caption><table>  Thursday Night   <tr></tr>    <th>7:30</th>    <th>8:00</th>    <th>8:30</th>  <tr>    <td>Shindig</td>    <td>Donna Reed Show</td>    <td>Bewitched</td>  <tr>    <colspan="2">Laredo</colspan>    <td>Daniel Boone</td>  </tr></table>

Element Review: Tables

The following is a summary of the elements we covered in this chapter.

Element and attributes

Description

table

Establishes a table element

tr

Establishes a row within a table

td

Establishes a cell within a table row

colspan="number"

Number of columns the cell should span

rowspan="number"

Number of rows the cell should span

headers="header name"

Associates the data cell with a header

th

Table header associated with a row or column

abbr="text"

Alternative label for when the header cell is referenced in other contexts

colspan="number"

Number of columns the cell should span

rowspan="number"

Number of rows the cell should span

headers="header name"

Associates a header with another header

scope="row|col|rowgroup|colgroup"

Associates the header with a row, row group, column, or column group

caption

Gives the table a title that displays in the browser

colgroup

Declares a group of columns

span="number"

Number of columns the column group spans; may not be used when the colgroup contains col elements

col

Declares a column

span="number"

Number of columns the column spans

tbody

Identifies a table body row group

thead

Identifies a table header row group

tfoot

Identifies a table footer row group