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. |
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
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.
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.
- A data cell cannot occur above, or to the left of, a header cell.
- A cell that contains text cannot span more columns than a cell that occurs above it.
- A cell that contains text cannot span more rows than a cell that occurs to its left.
- The indentation of the row headers accurately indicates the hierarchical order of those headers.
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.
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
andHEADER
attributes of "TH" and "TD" tags, and in theHREF
,ID
, andNAME
attributes of "A" (anchor) tags.The format for internal identifiers is:
<table_identifier>.<identifier_type>.<period-separated-list>
- 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.
- 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
- 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.
- For a stub header cell, the third part of an internal identifier must be "0".
- 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 |
The rules for irregular tables are the same as the rules for regular tables, with the following additions.
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> </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>
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> </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>
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>
In order to discuss footnotes clearly, we need to introduce some technical terms.
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".
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.
A presentation footnote ID can play two different roles, or perform two different functions, depending on its location in a table.
- When it appears in the body of a table, it performs the function of a footnote reference, referring to a footnote.
- When it appears in the FOOTNOTES part of a table, it performs the function of a footnote target.
<span
class="footnote">(1)</span>
<a href="#table6.f.1"><span
class="footnote">(1)</span></a>
(1,2,3)
is deprecated. <span
class="footnote">(1)</span>
<a id="table6.f.1"><span
class="footnote">(1)</span></a>
Footnotes:
enclosed in STRONG
tags.
<strong>Footnotes:</strong><br>
<A ID="table3.f.1" NAME="table3.f.1"> <SPAN
CLASS="footnote">(1)</SPAN></A> 1869=100
<BR>
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:
- 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".
- The base footnote ID of an endnote may contain multiple words separated by whitespace. For example: "
Note to Users
".
Ruritanian Raw Materials Production | 2001 | 2002 (p) |
---|---|---|
Iron Ore (2) |
999.99 |
999.99 |
(4) 999.99 |
- |
|
Footnotes: |
<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>
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
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>
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.
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).
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 |