Article
Terrific Tables with CSS
Table Elements in Action
With all the details out of the way, let’s take a look at some examples of tables—how spiffy can we make them with the careful application of the styling we’ve learned?
Adding Style to Tabular Calendars
Calendars love tables. In fact, the two are a match made in heaven, what with calendars adapting their weeks so well to a series of rows, and their days to columns. Here’s a completely unadorned table:
Figure 13. Unstyled calendar

This is an okay and perfectly functional table, except that it’s arguably a bit dull; my need to promote my birthday has thrown out the balance just a smidgen, too. Let’s take a look at the markup and think about what it’ll take to give this table a bit more style andje ne sais qua:
Example 6. calendar.html (excerpt)
<table>
<caption>June</caption>
<col class="weekend" />
<col class="weekday" span="5" />
<col class="weekend" />
<thead>
<tr>
<th>Sun</th>
<th>Mon</th>
<th>Tue</th>
<th>Wed</th>
<th>Thu</th>
<th>Fri</th>
<th>Sat</th>
</tr>
</thead>
<tbody>
<tr>
<td><div class="day">1</div></td>
<td><div class="day">2</div></td>
<td><div class="day">3</div></td>
<td><div class="day">4</div></td>
<td><div class="day">5</div></td>
<td class="birthday"><div class="day">6</div>
<div class="notes">It’s my birthday!</div></td>
<td><div class="day">7</div></td>
</tr>
...
</tbody>
</table>
We’ll specifically add a div around each day number. This allows additional items to be added to a day, and leaves us the flexibility of styling the number itself. More general styles, such as holidays, are applied to the table cell—let’s apply an appropriately stand-out style to my birthday!
To make this look more like a calendar, we can set up a number of styles. We’ll style a larger caption , causing the month to stand out more prominently. Each day is given a height and width, allowing room to add notes. The weekend columns have been set up to stand out from weekdays, and we can designate holidays and birthdays as special.
Here’s the CSS for our calendar table:
Example 7. calendar.html (excerpt)
table {
border: 1px solid #999;
border-collapse: collapse;
font-family: Georgia, Times, serif;
}
th {
border: 1px solid #999;
font-size: 70%;
text-transform: uppercase;
}
td {
border: 1px solid #999;
height: 5em;
width: 5em;
padding: 5px;
vertical-align: top;
}
caption {
font-size: 300%;
font-style: italic;
}
.day {
text-align: right;
}
.notes {
font-family: Arial, Helvetica, sans-serif;
font-size: 80%;
text-align: right;
padding-left: 20px;
}
.birthday {
background-color: #ECE;
}
.weekend {
background-color: #F3F3F3;
}
Once we’ve combined our modified markup and the style sheet, we get a much more aesthetically pleasing calendar.
Figure 14. Styled calendar
![]()
Striping Table Rows
Striping, also known as zebra tables, is useful on large monotonous sets of data as it helps improve the readability. Too much text without delineation can make it difficult to see where one column lines up with another within a particular row.
To achieve striping, we simply add a class to every other row (check out the section called “The Future” at the end of this chapter for an even cooler, although less supported, approach to striping):
...
<tr>
<td>...</td>
<td>...</td>
<td>...</td>
</tr>
<tr class="even">
<td>...</td>
<td>...</td>
<td>...</td>
</tr>
...
Our base styles would be applied to the normal tr and then alternate styles applied to the tr elements that have a class of even:
Example 8. striped.html (excerpt)
tr {
background-color: #FEE;
}
tr.even {
background-color: #EEF;
}
In this example, the odd rows will be a light red and the even rows will be a light blue, as shown:
Figure 15. Different background-colors on alternate rows for an easier-to-read table
![]()
Another option I often choose when striping my tables is to use a semi-transparent PNG as a background image for the alternate rows, as I’ve done in Figure 16, below. Taking this route allows me to change out the background-color (or background-image) on the table without having to worry about sizing or color matching issues:
Example 9. striped-png.html (excerpt)
tbody tr.odd td {
background: transparent url(images/tr_bg.png) repeat top left;
}
Figure 16. Striping alternate rows using a semi-transparent PNG
![]()
PNG, pronounced “ping,” is an image-format type just like GIF or JPG, but it also supports a graduated transparency. In other words, the background can be made partially visible through parts of the image, like looking through a foggy glass window. Conversely, GIF only supports index transparency where there are no levels of opaqueness—it’s either on or it’s off. JPG doesn’t support transparency at all. Most graphics software, such as Adobe Fireworks, The GIMP, or Adobe Photoshop, will happily export PNGs, so it’s a very useful image format to have up your sleeve.
I normally export a PNG that is just white and is set to a transparency of about 15% to 25% as this provides a semi-transparent overlay that’ll work in the context of almost any color scheme.
As discussed in Chapter 2 of The Art and Science of CSS, Internet Explorer didn’t support PNG background images prior to version 7. Once again, we’ll work around the problem by using Internet Explorer’s proprietary conditional comments:
Example 10. striped-ong.html (excerpt)
<!--[if lt IE 7]>
<style type="text/css" media="screen">
tr.even {
background: none;
filter: progid:DXImageTransform.Microsoft.AlphaImageLoader
(src='images/tr_bg.png', sizingMethod='scale');
}
</style>
<![endif]-->
“But, hang on a second,” you ask. “Why use a PNG instead of the opacity CSS property?” Well, setting opacity in CSS might seem like the obvious choice for achieving this effect; the trouble is, it applies the opacity to all elements contained within. Therefore, all text and images would also be see-through. Setting opacity on an element requires some additional trickery to make it compatible with Internet Explorer, as well as causing your CSS to be invalid—unless the proprietary filter property is set via IE conditional comments:
td {
opacity: 0.2;
filter: alpha(opacity=20);
}
The opacity property is supported in Safari, Firefox, and Opera, and is part of the CSS 3 specification. To accomplish the same result in Internet Explorer, you have to use the proprietary filter property, which lets you specify an alpha filter.
You’ve stuck with me this far into the chapter, so it’s time I let you in on a little secret of mine. One trick I’ve often used is to have an image applied to the table background that eventually fades off to a solid color:
table {
width: 450px;
color: #FFF;
background: #333 url(images/table_bg.png) no-repeat top left;
border-collapse: collapse;
border: 8px solid #9C0;
}
This small exercise can give your table some crazy flair while still being an extremely solid cross-browser solution. Using this effect, in combination with the alpha PNGs, can allow you to create some very sexy tables!
The background-image is set on the table, which, as we covered in the section called “Applying Successful Backgrounds”, is reliable across all browsers. The image is specifically designed to fade out at the bottom to a solid color, which you can see below. In the example, we fade to a solid gray.
Figure 17. Combining alpha-PNG row striping with gradient background
![]()
So, there it is—I’m sure you’ll agree that a judiciously applied bit of CSS can make the most boring calendar bounce into a layout worthy of a wallplanner, with styling that allows latitude for creativity. Likewise, striping is a simple enough application, but produces a great effect that can be widely used in many table applications to give the most pedestrian content a colorful edge. It doesn’t end there, though.
Using JavaScript
As you’ve seen so far, CSS is fantastic for giving our tables some sexy sizzle. What can really send our tables over the edge is some nice JavaScript!
If you’ve been developing with web standards for some time, you’ve most likely come across the mantra: “separate your presentation from your content.” There’s a third spoke to this web-standards wheel, and that is behavior. Behavior is best handled through unobtrusive JavaScript. Unobtrusive JavaScript is having your scripts reside in an external file (just like a style sheet) that hooks itself into your HTML document.
Using unobtrusive JavaScript keeps your HTML clean and easily accessible, even for those users who don’t have JavaScript or have it turned off. The content itself will still be available and accessible for these users, who are, after all, in the minority; meanwhile, those users who have JavaScript turned on will be able to take advantage of the additional features you’ve enabled.
So, what can JavaScript do to pretty up our tables?
Row and Column Highlighting
A common feature is to add row highlighting support for Internet Explorer 6 (and lower). We can also take it to the next level and add column highlighting for all browsers.
If JavaScript isn’t your thing and the code in this example doesn’t make much sense, that’s okay. If you’re interested in learning JavaScript, I recommend that you grab a copy of the SitePoint book The JavaScript Anthology: 101 Essential Tips, Tricks, & Hacks which is an essential text in this area.
Let’s define a function that will run when the page loads. Thinking about our logic, we want this function to run any time a user moves the mouse over the table. More specifically, when the mouse is over a specific cell, it should change the background for that row and that column.
The first thing we need to do is to grab the table element and pass it into our highlight function.
Example 11. scripts/highlight.js (excerpt)
window.onload = function()
{
var tbl = document.getElementById('mytable');
setHighlight(tbl);
}
I’ve used window.onload, which is a really quick way to say that this block of code should run when the window has finished loading. Now, let’s see what the setHighlight function looks like.
Example 12. scripts/highlight.js (excerpt)
function setHighlight(table)
{
if (!table) return;
var TDs = table.getElementsByTagName("td");
for(var i = 0; i<TDs.length; i++) {
TDs[i].onmouseover = rowColHighlight;
TDs[i].onmouseout = rowColDelight;
}
}
Our highlight function will return to its origin if an element isn’t passed through to the function. If we have an element, it’ll attract all table cells within our table. It loops through them and attaches two events to each one. The rowColHighlight will be responsible for highlighting rows and columns when the user moves a mouse over a cell, and rowColDelight will be responsible for removing the highlight when the user moves the mouse out of a cell.
Example 13. scripts/highlight.js (excerpt)
function rowColHighlight()
{
highlighter(this, '#EEE');
}
function rowColDelight()
{
highlighter(this, '');
}
Our two functions just call another function but pass in two variables. The first is the element to be highlighted. The this keyword refers to the element that triggered the event—in our case, it’s the cell. The second variable is the color that we want for the highlighter.
The highlighter function is our meat and potatoes:
Example 14. scripts/highlight.js (excerpt)
function highlighter(cell, color)
{
cell.parentNode.style.backgroundColor = color;
var table = getTable(cell);
var col = table.getElementsByTagName("col");
col[cell.cellIndex].style.backgroundColor = color;
}
First, from the cell, we tell it to get the parentNode (the row element surrounding my cells) and change the background-color to the color that was passed in. Then, we tell it to get the table that surrounds the cell. Retrieving the table element can be a little trickier depending on how the HTML is setup so we’ve created another function to handle this. I’ll touch on this again shortly.
Once we have our table element, we grab all the col elements in the table and then grab the one that matches the column in which the cell resides. The cellIndex property is the number of columns up to and including the current cell. Once we have the right column, we assign it a style. This styling should work as long as no background is specified on the other cells, rows, or row groups.
Back to the getTable function that I skipped before:
Example 15. scripts/highlight.js (excerpt)
function getTable(obj)
{
while (obj && obj.tagName.toLowerCase() != 'table')
{
obj = getTable(obj.parentNode);
}
return obj;
}
This function takes the current element and checks to see whether it’s the table element. If it isn’t, then the function grabs the parent element and checks that. This checking process will continue until the function finds the table element or no element at all. Once the table is found, that table object is returned.
The next figure depicts our highlighting function in action.
Figure 18. Row and column highlighting compatible with most browsers
![]()
I should point out that the script makes a number of huge assumptions. To make your code more reliable, you should provide checking mechanisms to account for different scenarios. For example, one of the assumptions we’ve made here is that there would be the same number of col elements as there are cells in a row. This may not be the case if you used colgroup elements or the span attribute on other col elements. If any of those assumptions were incorrect, you’d be bound to see JavaScript errors.
Other Ideas
One of the other common responsibilities often relegated to JavaScript is table sorting. Table sorting is a very handy tool for your users, allowing them to manipulate the table view without requiring slow and repetitive page refreshes from the server. A quick search on Google for “table sorting” will yield a number of scripts, and I’ve used Stuart Langridge’s “sorttable script” with much success.
With the onslaught of Ajax—the ability to connect to and send and receive data from the server via JavaScript—you can even offer spreadsheet-like functionality including live editing. For a great example, check out Active Widgets Grid component. Speaking of things new and cutting-edge, by the way, what about CSS 3? Let’s indulge in some speculation of how this upcoming standard will revolutionize the way we style our tables.