Tables

One of the most complex structures in (X)HTML is the table. In its proper form, a table is essentially a two-dimensional list with the option of labels—more precisely, it is (X)HTML's version of the spreadsheet. The table uses quite a large set of elements.

All table elements work as block elements, and all use the core and language attributes.

Simple Tables

Most table elements consist of the table element, table rows, and table cells, with the option of a caption. These are simple tables.

Table Elements

The following elements will make for a perfectly suitable table.

The Table Element

Element Name: table

The table element contains all other elements associated with a table. It is a block element.

Implied Attributes
summary
The summary attribute contains a summary of the table's contents, in case a user agent has difficulty rendering a table (some do).

The Table Caption Element

Element Name: caption

The caption element—which is an optional element, but comes first in any table should you use it—is used to give your table a title.

The Table Row Element

Element Name: tr

Table cells cannot be stored directly in the table element—they must be arranged in rows. This is the job of the tr element: It creates the rows that the cells are in.

The Table Header Cell and Table Data Cell Elements

Element names:

th
Table Header Cell Element
td
Table Data Cell Element

These elements are paired together because they contain similar functions and attributes. They can contain either plain text (which is normally the case) or any body element—including tables. If you're nesting tables, though, you might be going overboard.

Implied Attributes
abbr
This contains an abbreviation of a header or data (usually header) cell's contents. This is sometimes used by a user agent (such as a speech synthesizer) if it reads out a cell's header before its contents.
axis
Sometimes you need to catagorize elements beyond row or column. That's what this is for, and is best used as a comma-seperated list of catagory names, though the validator won't squawk if it uses a different format.
colspan
For various reasons, sometimes a cell is supposed to span two or more columns (stretching it horizontally). This attribute is numerical.
headers
This attribute is an IDREFS-type attribute—in other words, it's a reference to an ID attribute elsewhere. It's usually used in a td element to reference the id attribute in the th at the head of the td element's column.
rowspan
For various reasons, sometimes a cell is supposed to span two or more rows (stretching it vertically). This attribute is numerical.
scope
This, again, is usually used with table header cells. It is an enumerated attribute with four values that describe what it applies to:
  • row
  • col
  • rowgroup
  • colgroup
The values row and col refer to a header cell's respective row and column (respectively). I'll get to rowgroup and colgroup shortly.

An Example Table

To demonstrate tables, I've included one that would look exactly how it would normally, and the same table with borders added so that you can see just what is going on.

The Code Of the Above Table
<table summary="Compares binary, octal, decimal, and hexadecimal notation.">
<caption>Binary, Octal, Decimal, Hexadecimal</caption>
<tr>
<th id="BIN">Binary</th>
<th id="OCT">Octal</th>
<th id="DEC" rowspan="2">Decimal</th>
<th id="HEX">Hexadecimal</th>
</tr>
<tr>
<th headers="BIN">(Base 2)</th>
<th headers="OCT">(Base 8)</th>
<th headers="HEX">(Base 16)</th>
</tr>
<tr>
<td headers="BIN OCT DEC HEX" colspan="4">0</td>
</tr>
<tr>
<td headers="BIN OCT DEC HEX" colspan="4">1</td>
</tr>
<tr>
<td headers="BIN">10</td>
<td headers="OCT DEC HEX" colspan="3">2</td>
</tr>
<tr>
<td headers="BIN">11</td>
<td headers="OCT DEC HEX" colspan="3">3</td>
</tr>
<tr>
<td headers="BIN">100</td>
<td headers="OCT DEC HEX" colspan="3">4</td>
</tr>
<tr>
<td headers="BIN">101</td>
<td headers="OCT DEC HEX" colspan="3">5</td>
</tr>
<tr>
<td headers="BIN">110</td>
<td headers="OCT DEC HEX" colspan="3">6</td>
</tr>
<tr>
<td headers="BIN">111</td>
<td headers="OCT DEC HEX" colspan="3">7</td>
</tr>
<tr>
<td headers="BIN">1000</td>
<td headers="OCT">10</td>
<td headers="DEC HEX" colspan="2">8</td>
</tr>
<tr>
<td headers="BIN">1001</td>
<td headers="OCT">11</td>
<td headers="DEC HEX" colspan="2">9</td>
</tr>
<tr>
<td headers="BIN">1010</td>
<td headers="OCT">12</td>
<td headers="DEC">10</td>
<td headers="HEX">A</td>
</tr>
<tr>
<td headers="BIN">1011</td>
<td headers="OCT">13</td>
<td headers="DEC">11</td>
<td headers="HEX">B</td>
</tr>
<tr>
<td headers="BIN">1100</td>
<td headers="OCT">14</td>
<td headers="DEC">12</td>
<td headers="HEX">C</td>
</tr>
<tr>
<td headers="BIN">1101</td>
<td headers="OCT">15</td>
<td headers="DEC">13</td>
<td headers="HEX">D</td>
</tr>
<tr>
<td headers="BIN">1110</td>
<td headers="OCT">16</td>
<td headers="DEC">14</td>
<td headers="HEX">E</td>
</tr>
<tr>
<td headers="BIN">1111</td>
<td headers="OCT">17</td>
<td headers="DEC">15</td>
<td headers="HEX">F</td>
</tr>
<tr>
<td headers="BIN">10000</td>
<td headers="OCT">20</td>
<td headers="DEC">16</td>
<td headers="HEX">10</td>
</tr>
</table>

Complex Tables

Simple tables as described above are essentially a shortcut. A complete table is divided into several sections, depending on content. Note: The caption element (if you use it) always goes before any of the following elements.

The core and language attributes may be used with all elements mentioned here.

Table Section Elements

Tables are divided into three main sections, depending on their content: the table head, the table foot, and the table body. All three elements must have the same number of columns, or your table won't look right (using the colspan attribute counts).

By the way, when I mentioned the rowgroup value of the scope attribute, these sections are what that value refers to.

The Table Header Element

Element Name: thead

The thead element is used to contain the column headers. You may use only one thead element.

The Table Footer Element

Element Name: tfoot

The tfoot element is an optional element and appears at the bottom of the table. It is usually used for footnotes or even a reiteration of table headers if the table is particularly long. Even though it appears at the bottom of the table, in the code, the tfoot element goes before the table body.

The reason for this is an HTML document is always processed from top to bottom. (This is why you should have your base element before your link elements.) Thus, if the browser fails to download the entire table (which is possible), the tfoot element will still be processed.

You may use only one tfoot element.

The Table Body Element

Element Name: tbody

The tbody element contains the main body of your table data. You may use as many tbody elements as you please, so long as you have at least one.

Table Columns

Aside from rows, tables also use columns, which control how cells in specific columns look: their width, height, the vertical alignment of their contents, and so on. Nowadays you can use Cascading Style Sheets to control this, but columns are still a part of the HTML 4.01 Strict specification so I'm mentioning them here, along with their more common attributes.

The Column Element

Element Name: col

The col element normally controls the appearance of a single column (unless more are specified).

The col element is an empty element. It has no end tag.

Implied Attributes
span
This numeric attribute allows a col element to control more than one column.
width
This numeric attribute sets the width of the column in pixels.

The Column Group Element

Element Name: colgroup

The colgroup element is used to put col elements into groups. It can also be used on its own. The columns that fall under this element's range are what the colgroup value of the scope element refers to.

Implied Attributes
span
This numeric attribute allows a col element to control more than one column.
width
This numeric attribute sets the width of the column in pixels.

Usage Of colgroup And col Elements

The colgroup and col elements go after the caption element but before any others.

You may have col elements on their own, colgroup elements without children, or col elements nested in colgroup elements. What may not be done is a mixture of colgroup elements and lone col elements.

In other words, the first two following examples are valid, but the third is not.

col Elements On Their Own
<col width="100">
<col span="2" width="200">
<col width="100">
colgroup Elements
<colgroup width="100">
<col>
<col span="2" width="200">
<col>
</colgroup>
<colgroup span="3" width="150">
</colgroup>
col Elements Outside colgroup Elements (Invalid)
<col width="100">
<col span="2" width="200">
<col width="100">
<colgroup width="100">
<col>
<col span="2" width="200">
<col>
</colgroup>
<colgroup span="3" width="150">
</colgroup>

An Example Complex Table

To demonstrate tables, I've included one that would look exactly how it would normally, and the same table with borders added so that you can see just what is going on. Do note the disadvantage of using multiple tbody elements: the rowspan attribute will not allow a cell to stretch beyond its ancestor tbody element.

The Code Of the Above Table
thead, tfoot, and tbody Tags Are In Italics.
The Column Elements Are Underlined.
<table summary="Compares binary, octal, decimal, and hexadecimal notation.">
<caption>Binary, Octal, Decimal, Hexadecimal</caption>
<colgroup>
<col span="2" width="100">
<col span="3" width="60">
</colgroup>
<thead>
<tr>
<th class="RowHead">Number System:</th>
<th id="BIN">BIN</th>
<th id="OCT">OCT</th>
<th id="DEC">DEC</th>
<th id="HEX">HEX</th>
</tr>
</thead>
<tfoot>
<tr>
<th class="RowHead">DIGITS:</th>
<td headers="BIN">0, 1</td>
<td headers="OCT">0, 1, 2, 3, 4, 5, 6, 7</td>
<td headers="DEC">0, 1, 2, 3, 4, 5, 6, 7, 8, 9</td>
<td headers="HEX">0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F</td>
</tr>
<tr>
<td colspan="5">
<dl>
<dt>BIN</dt>
<dd>Binary (Base 2)</dd>
<dt>OCT</dt>
<dd>Octal (Base 8)</dd>
<dt>DEC</dt>
<dd>Decimal (Base 10)</dd>
<dt>HEX</dt>
<dd>Hexadecimal (Base 16)</dd>
</dl>
</td>
</tr>
</tfoot>
<tbody id="BIN_OCT_DEC_HEX_Match">
<tr>
<th class="RowHead" rowspan="2">Number Comparisons</th>
<td headers="BIN OCT DEC HEX" colspan="4">0</td>
</tr>
<tr>
<td headers="BIN OCT DEC HEX" colspan="4">1</td>
</tr>
</tbody>
<tbody id="OCT_DEC_HEX_Match">
<tr>
<th class="RowHead" rowspan="6">Number Comparisons</th>
<td headers="BIN">10</td>
<td headers="OCT DEC HEX" colspan="3">2</td>
</tr>
<tr>
<td headers="BIN">11</td>
<td headers="OCT DEC HEX" colspan="3">3</td>
</tr>
<tr>
<td headers="BIN">100</td>
<td headers="OCT DEC HEX" colspan="3">4</td>
</tr>
<tr>
<td headers="BIN">101</td>
<td headers="OCT DEC HEX" colspan="3">5</td>
</tr>
<tr>
<td headers="BIN">110</td>
<td headers="OCT DEC HEX" colspan="3">6</td>
</tr>
<tr>
<td headers="BIN">111</td>
<td headers="OCT DEC HEX" colspan="3">7</td>
</tr>
</tbody>
<tbody id="DEC_HEX_Match">
<tr>
<th class="RowHead" rowspan="2">Number Comparisons</th>
<td headers="BIN">1000</td>
<td headers="OCT">10</td>
<td headers="DEC HEX" colspan="2">8</td>
</tr>
<tr>
<td headers="BIN">1001</td>
<td headers="OCT">11</td>
<td headers="DEC HEX" colspan="2">9</td>
</tr>
</tbody>
<tbody id="No_Match">
<tr>
<th class="RowHead" rowspan="7">Number Comparisons</th>
<td headers="BIN">1010</td>
<td headers="OCT">12</td>
<td headers="DEC">10</td>
<td headers="HEX">A</td>
</tr>
<tr>
<td headers="BIN">1011</td>
<td headers="OCT">13</td>
<td headers="DEC">11</td>
<td headers="HEX">B</td>
</tr>
<tr>
<td headers="BIN">1100</td>
<td headers="OCT">14</td>
<td headers="DEC">12</td>
<td headers="HEX">C</td>
</tr>
<tr>
<td headers="BIN">1101</td>
<td headers="OCT">15</td>
<td headers="DEC">13</td>
<td headers="HEX">D</td>
</tr>
<tr>
<td headers="BIN">1110</td>
<td headers="OCT">16</td>
<td headers="DEC">14</td>
<td headers="HEX">E</td>
</tr>
<tr>
<td headers="BIN">1111</td>
<td headers="OCT">17</td>
<td headers="DEC">15</td>
<td headers="HEX">F</td>
</tr>
<tr>
<td headers="BIN">10000</td>
<td headers="OCT">20</td>
<td headers="DEC">16</td>
<td headers="HEX">10</td>
</tr>
</tbody>
</table>