HTML Tables Cookbook

HOME

Creative Commons License Licensed under Creative Commons
Attribution 3.0 Unported License
Stephen R. Ferg
2003-02-06
For the best results when printing this page,
set your browser's font size to SMALLEST.
The material in the "Accessible HTML Tables" Sourceforge project is licensed under the Creative Commons "attribution" license. You are free to translate it and post your translation on your own web site subject to the following conditions. (1) You acknowledge the original work and the original author. (2) You do not suggest that the original author endorses your translation. (3) You do not request that a link to your translation be added to this site.

Table of Contents

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

1 Introduction

This page presents a set of procedures that a Federal agency might use in designing its HTML data tables. I believe that HTML tables that follow these procedures and guidelines will be in compliance with the provisions of section 508.

Note that this material reflects my own personal opinions and interpretation of the W3C HTML specification, and is not in any way endorsed by LABSTAT, by BLS, or by the W3C. Note furthermore that the text of these papers was frozen in 2002, but the HTML language specification has continued to evolve. As of February 2012, I don't believe that the evolutionary changes to HTML have obsoleted any of these guidelines. But it is possible that, as HTML continues to evolve, some of these guidelines may become obsolete.

I wrote these papers during the summer of 2002, while working as a computer systems analyst in the Division of Enterprise Web Services (DEWS, aka LABSTAT) at the United States Bureau of Labor Statistics (BLS) in Washington, DC, USA. I am no longer active in the area of accessibility and HTML, but I hope that developers working in this area will continue to find these guidelines useful for at least a few more years.
Stephen Ferg, February 2, 2012


2 Terms Used in this Document

2.1 Empty Cell

An empty cell in an HTML table is a TD or TH cell that contains no presentation content. In the underlying HTML code, the cell contains only whitespace or   elements. The expression cell that contains text refers to a cell that is not empty, that is, to a cell that contains presentation content.

2.2 Regular Table

A regular table is a table in which the header cells are positioned along the top of the table (as column header cells) and down the left side of the table (as row header cells). If the table contains multiple levels to the row or column headers, then the row and column header cells are arranged in strict hierarchic order.

More precisely, a regular table is defined as a table with the following characteristics.

2.3 Irregular Table

An irregular table is a table that breaks one or more of the rules for regular tables. Note in particular, that the use of a non-empty cell that spans more columns than a cell that occurs above it, will make a table irregular.

2.4 Internal identifiers

Internal identifiers are identifiers that are invisible to users. They are used in the HTML code for Web pages to support hyperlinks.

Several of the techniques discussed in this paper require the use if internal identifiers. Internal identifiers are used in the ID and HEADER attributes of "TH" and "TD" tags, and in the HREF, ID, and NAME attributes of "A" (anchor) tags.

The format for internal identifiers is:

<table_identifier>.<identifier_type>.<period-separated-list>
  1. The first part of an internal identifier must be a table identifier. The table identifier must begin with a lower-case letter, and may contain only digits, lower-case letters, and underscores.
  2. The second part of an internal identifier is an identifier type. It must be one of the following:
    • lower-case letter "c" for column header cells
    • lower-case letter "r" for row header cells
    • lower-case letter "f" for footnotes
  3. For column or row header cells, the third part of an internal identifier must be a period-separated list of integers. The integers must be in hierarchical order, beginning with the highest level and proceeding to the lowest level.
  4. For a stub header cell, the third part of an internal identifier must be "0".
  5. For a footnote, part 3 must be either an integer or a single lower-case letter.

Here are some examples.

    ID   Comment
  tablex.c.2   column header in tablex for a category that is the second category in the first row of column headers  
  tablex.r.1.2.13.2       row header in tablex for a category that is four levels deep in the category structure  
  tablex.r.0   stub header in tablex
  tablex.f.1   footnote 1 in tablex
  tablex.f.p   footnote p in tablex

3 Rules for HTML Tables

3.1 Rules for Regular Tables

  1. All tables must conform to the HTML 4.01 recommendation.
  2. Create a <CAPTION> element to hold the title of the table.
  3. Store every piece of data in a TD cell.
  4. Store every row and column header in a TH cell.
  5. Greenbar -- If the table is generated by a human being ("by hand") then the use of greenbar is optional. If the table is generated automatically, then the use of greenbar is required, and must follow the rules for greenbar (see below).
  6. The rows containing column header rows will be contained by a THEAD element.
  7. The rows containing body header rows will be contained by a TBODY element.
  8. If the row headings are nested (are broken down into multiple levels), then follow the procedures for nested row headings (see below).
  9. If the file contains footnotes, then follow the procedures for footnotes (see below).

3.2 Rules for Irregular Tables

The rules for irregular tables are the same as the rules for regular tables, with the following additions.

  1. The use of a HEADERS attribute is required for TD cells that span multiple columns. The HEADERS attribute on the TD cells must contain internal identifiers for all of the header cells that contain header information for the cell. Note that this includes row headers as well as column headers, and (if the table contains nested headers) internal identifiers for nested headers all the way up to the root.

4 Some Example Tables

4.1 Example Table 1a

Example Table 1a: Ruritanian Ore Production
  2000

Iron Ore

999.99


<TABLE CELLSPACING="0" CELLPADDING="0" BORDER="1" >
<CAPTION><SPAN CLASS="tableTitle">Example Table 1a: Ruritanian Ore
Production </SPAN></CAPTION>
<THEAD>
    <TR>
    <TH>&nbsp;</TH>
    <TH>2000</TH>
    </TR>
</THEAD>
<TBODY>
    <TR>
    <TH><P CLASS="sub0">Iron Ore</P> </TH>
    <TD><P CLASS="dataCell">999.99</P> </TD>
    </TR>
</TBODY>
</TABLE>

4.2 Example Table 1b

Example Table 1b: Ruritanian Ore Production
  2000 2001 2002

Iron Ore

999.99

999.99

999.99

Copper Ore

999.99

999.99

999.99

Tin Ore

999.99

999.99

999.99


<TABLE CELLSPACING="0" CELLPADDING="0" BORDER="1" >
<CAPTION><SPAN CLASS="tableTitle">Example Table 1b: Ruritanian Ore
Production </SPAN></CAPTION>
<THEAD>
    <TR>
    <TH>&nbsp;</TH>
    <TH>2000</TH>
    <TH>2001</TH>
    <TH>2002</TH>
    </TR>
</THEAD>

<TBODY>
    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~ Iron ~~~~~~~~~~~~~~~~~~~~~ -->
    <TR>
    <TH><P>Iron Ore</P> </TH>
    <TD>
    <P CLASS="dataCell">999.99</P> </TD>
    <TD><P CLASS="dataCell">999.99</P> </TD>
    <TD><P CLASS="dataCell">999.99</P> </TD>
    </TR>

    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~ Copper ~~~~~~~~~~~~~~~~~~~~~ -->
    <TR CLASS="greenbar">
    <TH><P>Copper Ore</P> </TH>
    <TD><P CLASS="dataCell">999.99</P> </TD>
    <TD><P CLASS="dataCell">999.99</P> </TD>
    <TD><P CLASS="dataCell">999.99</P> </TD>
    </TR>

    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~ Tin ~~~~~~~~~~~~~~~~~~~~~ -->
    <TR>
    <TH><P>Tin Ore</P> </TH>
    <TD><P CLASS="dataCell">999.99</P> </TD>
    <TD><P CLASS="dataCell">999.99</P> </TD>
    <TD><P CLASS="dataCell">999.99</P> </TD>
    </TR>

</TBODY>
</TABLE>





4.3 Example Table 1c

Example Table 1c: Ruritanian Ore Production
Ruritanian Raw Materials Production By Weight By Value
Adjusted Unadjusted Adjusted Unadjusted
2001 2002 2001 2002 2001 2002 2001 2002

Iron Ore

999.99

999.99

999.99

999.99

999.99

999.99

999.99

999.99

Copper Ore

999.99

999.99

999.99

999.99

999.99

999.99

999.99

999.99

Tin Ore

999.99

999.99

999.99

999.99

999.99

999.99

999.99

999.99


<table cellspacing="0" cellpadding="0" border="1" >
<caption><span class="tableTitle">Example Table 1c: Ruritanian Ore Production </span></caption>

<thead>
<tr>
    <th class="stubhead" rowspan="3">Ruritanian Raw Materials Production</th>
    <th colspan="4">By Weight</th>
    <th colspan="4">By Value</th>
</tr>

<tr>
    <th colspan="2">Adjusted</th>
    <th colspan="2">Unadjusted</th>
    <th colspan="2">Adjusted</th>
    <th colspan="2">Unadjusted</th>
</tr>

<tr>
    <th>2001</th>
    <th>2002</th>
    <th>2001</th>
    <th>2002</th>
    <th>2001</th>
    <th>2002</th>
    <th>2001</th>
    <th>2002</th>
</tr>
</thead>

<tbody>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~ Iron ~~~~~~~~~~~~~~~~~~~~~ -->
<tr>
    <th><p class="sub0">Iron Ore</p></th>
    <td><p class="dataCell">999.99</p></td>
    <td><p class="dataCell">999.99</p></td>
    <td><p class="dataCell">999.99</p></td>
    <td><p class="dataCell">999.99</p></td>
    <td><p class="dataCell">999.99</p></td>
    <td><p class="dataCell">999.99</p></td>
    <td><p class="dataCell">999.99</p></td>
    <td><p class="dataCell">999.99</p></td>
</tr>

<... snip ...>

</tbody>
</table>


5 Rules for Footnotes

In order to discuss footnotes clearly, we need to introduce some technical terms.

5.1 Base Footnote ID

A base footnote ID is the letter, number, or word that is used to identify a footnote. Examples of base footnote IDs are "1", "2", "p", and "Note".

5.2 Presentation Footnote ID

A presentation footnote ID is what the user sees when he looks at a footnote identifier. A presentation footnote ID is composed of the base footnote ID , enclosed in parentheses, and then enclosed in SPAN tags with an attribute of class="footnote". Here is an example:

You see HTML code Comment
1 1 the base footnote ID
(1) (1) the base footnote ID enclosed in parentheses
(1) <span class="footnote">(1)</span> the presentation footnote ID.

The base footnote ID may not contain any whitespace, and no whitespace is permitted between the base footnote ID and the enclosing parentheses, nor between the parentheses and the enclosing SPAN tags.

5.3 Two Different Roles for Presentation Footnote IDs - as Footnote References and Footnote Targets

A presentation footnote ID can play two different roles, or perform two different functions, depending on its location in a table.


A picture of a table. The picture labels visible footnote IDs in the body of the table as 'footnote references', and visible footnote IDs in the footnotes section as 'footnote targets'.



5.4 Footnote References

  1. The HTML coding for a footnote reference is a presentation footnote identifier wrapped in anchor ("A") tags. The anchor tag must contain an HREF attribute whose value is the pound sign (#) followed by the presentation footnote ID. For example, if the HTML for the presentation footnote ID is:
    <span class="footnote">(1)</span>
    then the HTML for the footnote reference is
    <a href="#table6.f.1"><span class="footnote">(1)</span></a>
  2. In a header cell, footnote references must occur at the right, or trailing, end of the text in the cell. In a header cell, each footnote reference must be preceded by whitespace.
  3. In a data cell, footnote references must occur at the left, or leading, end of the text in the cell. In a data cell, each footnote reference must be followed by whitespace.
  4. We want text within a table to be able to wrap, if that is the best way to display the the table. In keeping with that objective, whitespace used within a table must be "breaking" whitespace (the use of "&nbsp;" is forbidden) and the use of "nowrap" is forbidden.
  5. If a cell contains multiple footnote references, each footnote reference must be preceded (or followed, as appropriate) by whitespace.
  6. Note that the practice of formatting multiple footnote references as a comma-separated list enclosed in a single pair of parentheses (1,2,3) is deprecated.

5.5 Footnote Targets

  1. The HTML coding for a footnote target is a presentation footnote identifier wrapped in anchor ("A") tags. The anchor tag must contain an ID attribute whose value is the footnoted internal identifier. No whitespace is permitted between the <SPAN tags> and the enclosing <A> tags. For example, if the HTML for the presentation footnote ID is:
    <span class="footnote">(1)</span>
    then the HTML for the footnote reference is
    <a id="table6.f.1"><span class="footnote">(1)</span></a>
  2. If a table has footnotes, the footnotes will be placed in a single cell at the bottom of the table. This "footnotes cell" will span the entire width of the table.
  3. The first line of text in the footnotes cell will be Footnotes: enclosed in STRONG tags.
    <strong>Footnotes:</strong><br>

  4. Each footnote will consist of a footnote target, followed by a breaking space, followed by the text of the footnote, followed by a <br> tag.
    <A ID="table3.f.1" NAME="table3.f.1"> <SPAN CLASS="footnote">(1)</SPAN></A> 1869=100 <BR>

  5. If a footnote contains multiple paragraphs, the paragraphs will be separated by two <BR> tags, producing the presentation effect of a blank line between the paragraphs. (See the example table 3.)

5.6 Endnotes

The footnotes section may contain endnotes. An endnote is a special kind of footnote that applies to the table as a whole. The definition of an endnote is: a footnote for which the table contains no footnote references. Because the table contains no explicit references to endnotes, the footnote target for an endnote does not require an enclosing anchor tag to provide its HTML ID. Basically, the footnote target for an endnote consists only of the footnote's presentation footnote ID.

The base footnote ID for endnotes is typically "Note". Other rules governing endnotes are:

  1. A table may contain multiple endnotes with the same base footnote ID. For example, there may be multiple endnotes with the base footnote ID of "Note".
  2. The base footnote ID of an endnote may contain multiple words separated by whitespace. For example: "Note to Users ".

5.7 Example Table 2

Example Table 2: Ruritanian Ore Production (with footnotes)(1)
Ruritanian Raw Materials Production 2001 2002 (p)

Iron Ore (2)

999.99

999.99

Tin (2) (3)

(4) 999.99

-

Footnotes:
(1) 1869=100
(2) In Ruritanian thalers.
(3) Volcanic tin is not considered pure tin.
(4) estimated
(p) preliminary
(NOTE) This is an example of an endnote.

This is the second paragraph of a multi-paragraph note. abcdef and ghijkl, or maybe qxyz. abcdef and ghijkl, or maybe qxyz. abcdef and ghijkl, or maybe qxyz.
(NOTE) This is a second endnote with a base footnote ID of "NOTE".
(Note to Users) blah blah blah.


<table cellspacing="0" cellpadding="0" border="1" align="center">
<caption><span class="tableTitle">Example Table 2: Ruritanian Ore Production (a table with footnotes)<a href="#table3.f.1"><span class="footnote">(1)</span></a></span></caption>

<thead>
    <tr>
    <th class="stubhead">Ruritanian Raw Materials Production</th>
    <th>2001</th>
    <th>2002 <a href="#table3.f.1"><span class="footnote">(p)</span></a></th>
    </tr>
</thead>

<tbody>
<tr>
    <th id="table3.r.1"><p class="sub0">Iron Ore <a href="#table3.f.2"><span class="footnote">(2)</span></a></p></th>
    <td><p class="dataCell">999.99</p></td>
    <td><p class="dataCell">999.99</p></td>
</tr>

<tr class="greenbar">
    <th><p class="sub0">Tin <a href="#table3.f.2"><span class="footnote">(2)</span></a>
       <a href="#table3.f.3"><span class="footnote">(3)</span></a></p></th>
    <td><p class="dataCell"><a href="#table3.f.4"><span class="footnote">(4)</span></a> 999.99</p>
    </td><td><p class="dataCell">-</p></td>
</tr>
</tbody>

<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ FOOTNOTES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<tbody>
<tr><td colspan="3">
    <p class="footnotes"><span class="footnotesTitle">Footnotes:</span><br />
    <a id="table3.f.1" name="table3.f.1"><span class="footnote">(1)</span></a> 1869=100<br />
    <a id="table3.f.2" name="table3.f.2"><span class="footnote">(2)</span></a> In Ruritanian thalers.<br />
    <a id="table3.f.3" name="table3.f.3"><span class="footnote">(3)</span></a> Volcanic tin is not considered pure tin.<br />
    <a id="table3.f.4" name="table3.f.4"><span class="footnote">(4)</span></a> estimated<br />
    <a id="table3.f.p" name="table3.f.p"><span class="footnote">(p)</span></a> preliminary<br />
    <span class="footnote">(NOTE)</span> This is an example of an <i>endnote.</i><br />
    <br />
    This is the second paragraph of a multi-paragraph note. abcdef and ghijkl, or maybe qxyz. .snip..<br />
    <span class="footnote">(NOTE)</span> This is a second endnote with a base footnote ID of "NOTE".<br />
    <span class="footnote">(Note to Users)</span> blah blah blah.<br />
    </p>
</td></tr>
</tbody>
</table>

6 Rules for Nested Row Headers

In some cases the row (or stub) headers of a table may be arranged in only a single level. In other cases, they may be nested, like the indented entries in an outline. Example tables 1a through 1c are examples of tables with single-level row headers. In this section, we discuss tables whose row headers are nested.

When a table contains nested row headers, the headers can be seen as forming a set of one or more trees. Each tree has a top-level row header which is not indented at all. Under the top-level header there may be one or more subheaders. Subheaders in turn may contain a set of subheaders, and so on. A header that has no subheaders is called a bottom-level header.

Here are the rules for nested row headers

  1. The TH tag for a row header cell must have an HTML ID attribute, unless that row header is a bottom-level header.
  2. The TH tag for a bottom-level row header cell is allowed to have an HTML ID attribute, but it is not required to have one.
  3. The TH tag for a row header cell must have a HEADERS attribute, unless that row header is a top-level header.
  4. The TH tag for a top-level row header cell is allowed to have an HTML HEADERS attribute, but it is not required to have one.
  5. If a TH cell has a HEADERS attribute, the HEADERS attribute must contain the internal identifers of all of the higher-level headers in the tree. The identifiers must be listed in order, going upward to the top-level header.
Diagram showing relationships in HEADERS attributes of TH cells

6.1 Example Table 3

Example Table 3
Ruritanian Raw Materials Production 2001

Iron Ore

999.99

Refined

999.99

Unrefined

999.99

Less than 40% pure

999.99

40% or more pure

999.99

Tin Ore

999.99

Refined

999.99


<table cellspacing="0" cellpadding="0" border="1" align="center">
<caption><span class="tableTitle">Example Table 3: Ruritanian Ore Production
(a table with multi-level row headers)</span></caption>
<thead>
    <tr>
    <th class="stubhead">Ruritanian Raw Materials Production</th>
    <th>2001</th>
    </tr>
</thead>

<tbody>
    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~ Iron ~~~~~~~~~~~~~~~~~~~~~ -->
    <tr>
    <th id="table2.r.1"><p class="sub0">Iron Ore</p></th>
    <td><p class="dataCell">999.99</p></td>
    </tr>

    <tr class="greenbar">
    <th id="table2.r.1.1" headers="table2.r.1">
    <p class="sub1">Refined</p></th>
    <td><p class="dataCell">999.99</p></td>
    </tr>

    <tr>
    <th id="table2.r.1.2" headers="table2.r.1">
    <p class="sub1">Unrefined</p></th>
    <td><p class="dataCell">999.99</p></td>
    </tr>

    <tr class="greenbar">
    <th id="table2.r.1.2.1" headers="table2.r.1.2 table2.r.1 ">
    <p class="sub2">Less than 40% pure</p></th>
    <td><p class="dataCell">999.99</p></td>
    </tr>

    <tr>
    <th id="table2.r.1.2.2" headers="table2.r.1.2 table2.r.1 ">
    <p class="sub2">40% or more pure</p></th>
    <td><p class="dataCell">999.99</p></td>
    </tr>

    <!-- ~~~~~~~~~~~~~~~~~~~~~~~~ Tin ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
    <tr  class="greenbar">
    <th id="table2.r.2">
    <p class="sub0">Tin Ore</p></th>
    <td><p class="dataCell">999.99</p></td>
    </tr>

    <tr>
    <th id="table2.r.3.1" headers="table2.r.2"><p class="sub1">Refined</p></th>
    <td><p class="dataCell">999.99</p></td>
    </tr>
</tbody>
</table>

7 Rules for Visual Separator Rows

It is sometimes desirable to use empty cells in a table for the purposes of visual formatting. Such cells typically span multiple colums, creating a completely or partly blank row that acts as a visual separator between the part of the table above it, and the part of the table below it.

The presence of visual separator rows in a regular table will not cause the table to become irregular, as long as the separator cells conform to the rules for regular tables. In order to conform to the first rule for regular tables (A data cell cannot occur above, or to the left of, a header cell) separator cells must be TD (not TH) cells.

Example table 4 contains three separator rows. The first separator row spans the entire table and is completely blank. The rows for "Separator B" and "Separator C" have text in their row header cells, and empty data cells that span the width of the table body. "Separator B" and "Separator C" differ only in the alignment of their contents.

8 Rules for Greenbar

In tables created by human beings, the use of greenbar is optional. In tables that are generated automatically, the use of greenbar is required.

The placement of greenbar is determined by an imaginary greenbar row counter which is set to 0 by the last column header row, and reset to 0 by a separator row.

Even-numbered rows of the table (based on the greenbar row counter) must include a class="greenbar" attribute on the TR tag. This class will produce a shaded row (see example tables 1b, 1c, and 4).

8.1 Example Table 4

Example Table 4: Ruritanian Ore Production
Ruritanian Raw Materials Production 2001 2002 2003 2004

Iron Ore

999.99

999.99

999.99

999.99

Copper Ore

999.99

999.99

999.99

999.99

 

Tin Ore

999.99

999.99

999.99

999.99

Copper Ore

999.99

999.99

999.99

999.99

Separator B

 

Tin Ore

999.99

999.99

999.99

999.99

Copper Ore

999.99

999.99

999.99

999.99

Separator C

 

Tin Ore

999.99

999.99

999.99

999.99

Copper Ore

999.99

999.99

999.99

999.99

Tin Ore

999.99

999.99

999.99

999.99



[Page End]