In the last few years HTML tables have taken a beating with all manner of HTML "expert" decrying them as bad/evil/the cause of world hunger etc. However, like most things in this world tables in and of themselves are not evil but are a useful and important part of HTML and it's just that people have used them to lay out entire websites which led to difficult to maintain code.

To be fair, it was the only option at the time as CSS was only a far distant promise of what the web could be. However, over the last couple of years CSS has matured and is now the best tool for the job of laying out websites. Which free's up tables to be used for what they were designed - displaying tabular data.

Target audience

People new to HTML should be able to follow this tutorial from start to finish and learn how to use tables effectively in your websites. Advanced users can probably skim the 1st few pages but will hopefully learn something from the later parts.

Anatomy of a table

What do you mean anatomy of a table? I know what tables are - I've been using them for years to layout my websites.

Let's start with a very basic table. HTML tables (<table>) are made up of rows (<tr>) which are filled with cells (<td>), as opposed to columns filled with cells. So a very simple table is created using the code below and should look like this.

HTML CSS Result Screenshot

<table id="table1">
 <tr>

  <td>Cell a1</td>
  <td>Cell b1</td>
 </tr>

 <tr>
  <td>Cell a2</td>

  <td>Cell b2</td>
 </tr>

</table>
#table1 {
 border: 1px solid black;}
#table1 td{
 border: 1px solid black;}
	
Cell a1 Cell b1
Cell a2 Cell b2
Screenshot of table1 demo

So What next?

There is also another tag that we can use to denote a cell in a HTML table, This is the <th> tag - which denotes a table heading. So lets add some table headings to our table. Most browsers render <th> tags with centered bold text. We have also added a grey background.

HTML CSS Result Screenshot
<table id="table2">

 <tr>
  <th></th>
  <th>Col a</th>
  <th>Col b</th>

 </tr>
 <tr>
  <th>Row 1</th>
   .....
#table2 {
 border: 1px solid black;}
#table2 td{
 border: 1px solid black;}
#table2 th{
 background-color: gray;}
Col a Col b
Row 1 Cell a1 Cell b1
Row 2 Cell a2 Cell b2
Screenshot of table2 demo

That's all pretty easy

HTML was created to allow scientists to share information easily, and as scientific documents typically contain a large amount of tables they are usually all labeled with captions. And so the <caption> tag was added. The caption tag is placed just inside the <table>. The position of the caption (top, bottom, left or right) can be controlled through the align attribute or via the caption-side CSS attribute.

Note: Unfortunately Internet Explorer does not support the CSS attribute caption-side correctly and so for most situations we are stuck with using the align attribute in the caption tag.

HTML CSS Result Screenshot
<table id="table3">
 <caption>Table 3<caption>
 <tr>
  <th></th>

  <th>Col a</th>
   .....
#table3 {
 border: 1px solid black;}
#table3 td{
 border: 1px solid black;}
#table3 th{
 background-color: gray;}
Table 3
Col a Col b
Row 1 Cell a1 Cell b1
Row 2 Cell a2 Cell b2
Screenshot of table3 demo

Keep it coming

The next group of tags related to tables are the <thead>, <tbody>, and <tfoot> tags. These tags allow us to group the rows of a table and give those rows meaning.

<thead>
Specifies that the rows are part of the table header and it can contain multiple rows.. This allows us to use the tag as a contextual selector to format the cells inside it and in Gecko based browsers it also has the nice feature of reprinting the table header and footer on every page if a table spans multiple pages.
<tbody>
The body of the table is where all the content/data is stored. It is interesting to note that while there can only be one <thead> or <tfoot> in a table there may be multiple tbodies. Later I will show how multiple <tbody> tags can be used to format a table nicely.
<tfoot>
Similar to the <thead> there can only be one table footer but it can contain multiple rows. The <tfoot> rows must be appear before the <tbody> and it will still display at the foot of the table. The reason for this is to allow the user agent to render the table before it has received all the rows.
HTML CSS Result Screenshot
<table id="table4">

 <caption>Table 4</caption>

 <thead>
  <tr>..... </tr>
 </thead>

 <tfoot>
  <tr>..... </tr>

 </tfoot>
 <tbody>
  <tr>..... </tr>

 </tbody>
</table>
#table4 {
 border: 1px solid black;
 caption-side:bottom;}
#table4 thead{
 background-color:#36c;}
#table4 tbody td{
 border: 1px solid black;}
#table4 tbody th{
 background-color: gray;}
#table4 tfoot{
 background-color:#c63;}
Table 4
Col a Col b
Footer a Footer b
Row 1 Cell a1 Cell b1
Row 2 Cell a2 Cell b2
Screenshot of table4 demo

But I want to use columns not rows?

There is patchy support across browsers for the <col> and <colgroup> tags which should allow us to refer to and hence format columns easily.

Internet Explorer allows the most practical control over the columns as background colors, fonts and borders can be set controlled on a column level. Mozilla based browsers allow control of borders when the collapsed borders model is used (more on that later) but doesn't allow formatting of text or background colours or text alignment. The reason that Mozilla doesn't support all the formatting is that while these tags are specified in HTML4.01 it is not clearly specified how CSS2 should be able to format it and what the cascade order is for these tags.

HTML CSS Result Screenshot
<table id="table5">
 <caption>Table 5</caption>
 <col class="firstCol" />

 <col />

 <col class="lastCol" />
 <thead>
  <tr>..... </tr>
 </thead>

 <tfoot>

  <tr>..... </tr>
 </tfoot>
 <tbody>
  <tr>..... </tr>

 </tbody>
</table>
#table5 {
 border: 1px solid black;
 caption-side:bottom;}
#table5 col.firstCol{
 background-color:red;}
#table5 col.lastCol{
 background-color:green;}
Table 5
Col a Col b
Footer a Footer b
Row 1 Cell a1 Cell b1
Row 2 Cell a2 Cell b2
Screenshot of table5 demo

Borders

There are two border models - the reason being - like almost anything in the HTML/CSS world - is around for legacy reasons, i.e. the initial idea didn't work so we created a new one which is much better but not widely supported yet. I'll get the models in a minute but common to both are the different attributes that can be set

border-width
Obviously enough this is the width of border you want. This can be specified as one of thin, medium or thick however most people use a width specified in pixels.
border-color
Another rather obvious one - the color that the border will be - go figure.
border-style
The style of the border, one of none, hidden, dotted, dashed, solid, double, groove, ridge, inset and outset.

Seperated Borders Model

Seperated Borders model - note the double borders between cells
th 0th 1th 2th 3th 4
td 0td 1td 2td 3td 4
f 0f 1f 2f 3f 4
0 00 10 20 30 4
1 01 11 21 31 4
2 02 12 22 32 4
3 03 13 23 33 4
4 04 14 24 34 4

This is the legacy model where all table cells can have a border around them, and the table element can also have a border. This allows borders to be created with CSS in much the same way as borders are specified in HTML. This tehcnically allows us to specify any border combination we want - however in reality it means that it is very difficult to specify borders in the way we naturally think about them, i.e. a 1px border on between all rows and a 2px border around the entire table.

The reason is that we need to work out when borders are beside each other and hence shouldn't display. For example - to get a 1px border around every cell in a table we typically need to specify bottom and left borders on every cell and top and right on the table. This could work quite well using a wysiwyg html editor but is not a very efficient way to specify this simple information and if you are doing this by hand or programatically it quickly becomes very annoying.

Can specify borders on:
<table>, <th>, <td>
Spacing between cells
'border-spacing: 1px' allows gaps between cells. However this also leaves gaps in background colors. The border-spacing CSS attribute is not supported on all browsers so the alternative way is to use <table cellspacing="0">
Support
All browsers

Collapsed Borders Model

Collapsed Borders model
th 0th 1th 2th 3th 4
td 0td 1td 2td 3td 4
f 0f 1f 2f 3f 4
0 00 10 20 30 4
1 01 11 21 31 4
2 02 12 22 32 4
3 03 13 23 33 4
4 04 14 24 34 4

In this model borders are allowed to be specified on much wider array of elements - including rows, row groups, columns, and column groups. This means that multiple elements can have borders that should be displaying at the same time then then minor borders will not be displayed using a cascading type rule will be used. i.e. Thick borders will be shown instead of thin borders and the different styles have a specified precedence - 'hidden', 'double', 'solid', 'dashed', 'dotted', 'ridge', 'outset', 'groove', and the lowest: 'inset'.

This allows us to specify borders on the elements as we think about it, i.e. a single dashed line on all rows and a 2 px solid line around the table. In this model the borders will not double up if they are beside each other.

Can specify borders on:
<table>, <tr>, <th>, <td>, <col>, <colgroup>, <thead>, <tbody>, <tfoot>
Hidden borders
By specifying 'border:hidden' in any areas will hide all borders on that edge.
Support
Mozilla: full, IE 5.5+: partial, Konqueror: none
Different examples of the collapsed borders model
Row borders
1 01 11 2
2 02 12 2
3 03 13 2
Column Borders
1 01 11 2
2 02 12 2
3 03 13 2
Rows takes precendence over cols
1 01 11 2
2 02 12 2
3 03 13 2
but solid takes precedence over dotted
1 01 11 2
2 02 12 2
3 03 13 2
and we don't have to worry about double borders
1 01 11 2
2 02 12 2
3 03 13 2
style hidden will over-ride and remove all borders
1 01 11 2
2 02 12 2
3 03 13 2
Screenshot of collapsed demo 1 Screenshot of collapsed demo 2 Screenshot of collapsed demo 3 Screenshot of collapsed demo 4 Screenshot of collapsed demo 5 Screenshot of collapsed demo 6

At the moment Mozilla and the browsers based on it are the only browsers which support this model properly - more's the shame. However, Internet Explorer does have limited functionality in that the borders do work on a limited set of the table elements, i.e. <table>, <th>, <td> and collapse correctly when used in this way.

Conclusion

I hope this has highlighted some of the useful parts of the HTML table model which have been overlooked over the last 5 years. The reason it has been overlooked is ue to the fact that tables have historically been used with no borders to layout pages.

Hopefully people with far more design skills than me (read any) can take this information and put it to some use to create useful, functional and stylish tables.

Bibliography

Further Reading

  • Originally published - 10-Dec-03
  • Added 'Further Reading' section and minor style changes- 26-Dec-03
  • Cut and Paste error on table 5 fixed - 10-Jan-04