Scenario: you are the developer of a website for a band called The Tramps, who have embarked on a cross-Canada tour. To support this you have made a tour page on the site with a table of concert dates, with buttons to buy tickets for each venue. The HTML for the table is:
<table>
<caption>The Tramps Tour Dates - Subject To Change</caption>
<tr>
<th>Date
<th>Location
<th>Time
<tr>
<td>November 22
<td>The Flying Barstool, NB
<td>7.00 pm
<tr>
<td>November 28
<td>The Rusty Schooner, NS
<td>9.00 pm
<tr>
<td>December 24
<td>The Empty Jug, NS
<td>8.00 pm
<tr>
<td>December 25
<td>Lobstercatch Xmas Party
<td>8.00 pm
</table>
This is an entirely appropriate use of a table: the concert dates are truly tabular data, with relevant, related information in each row. As we add more tour dates, we realize that the table is becoming more difficult to read. Let’s add some basic CSS to customize the appearance of the table:
table {
border-collapse: collapse;
font-family: Calluna Sans, Arial, sans-serif;
}
caption {
font-size: larger;
margin-bottom: 1em;
}
th {
background: #000;
color: white;
}
td { padding: 1em; }
… but as we add still more dates, the table becomes harder and harder to read. We need a way to clearly distinguish between rows: a typical solution is to give every other row a different background color. With basic CSS we could create a class
to accomplish this, as the style is used repeatedly on the page, but is not applicable to every use of the <tr>
element:
tr.highlight {
background-color: #cc9;
}
We would then modify our HTML to match the affected rows to the CSS:
<table>
<caption>The Tramps Tour Dates - Subject To Change</caption>
<tr>
<th>Date
<th>Location
<th>Time
<tr>
<td>November 22
<td>The Flying Barstool, NB
<td>7.00 pm
<tr class="highlight">
<td>November 28
<td>The Rusty Schooner, NS
<td>9.00 pm
<tr>
<td>December 24
<td>The Empty Jug, NS
<td>8.00 pm
<tr class="highlight">
<td>December 25
<td>Lobstercatch Xmas Party
<td>8.00 pm
</table>
While there is nothing wrong with this approach - it works perfectly well if you know all of the dates and venues in advance, and nothing changes – the reality is that The Tramps are a new screamo metal band. The tour van breaks down, gigs are cancelled, the lead singer loses his voice for several days, new gigs are found on the road and added to the tour schedule, etc. In response, you are asked to constantly alter, add, and remove table rows. This disorders the table, forcing you to move and re-apply classes every time a change is made. Your attempt to make the tour schedule more legible will increase your workload exponentially.
Traditionally, this is where JavaScript or some other language has to be brought in, as basic CSS cannot handle iteration. In “traditional” CSS, elements on a web page could only have styles applied if they were in certain contexts or states, or if they had a class
or id
applied. There was nothing in CSS that allowed a statement like “make every fifth paragraph look this way.”
One of the first pieces of JavaScript I ever wrote was to accomplish exactly this result. It was more than two dozen lines of code written to achieve a single effect. You can now accomplish the same appearance in a single line of CSS, while reducing markup at the same time.
After removing the classes from the markup (reverting to the table code we had at the start of this lesson), replace the tr.highlight
declaration in the style sheet with this:
tr:nth-child(odd) {
background-color: #cc9;
}
n
is the first mathematical expression you learned as a child: it is the set of natural counting numbers (1, 2, 3, 4… ), incrementing to infinity. The term “child” comes from the concept of the Document Object Model: the fact that in a well-written, valid, HTML document, every element is a descendant of the html
element. The <head>
and <body>
elements are the two immediate “children” of the <html>
tag, just as table rows are children of the <table>
tag. (They are not, however, the only children of <table>
. The <caption>
tag is a child too, which is why we must specify that we are working on table rows in our style sheet declaration.)
(Note that the first row in our table, the one that contains our table headers, does not appear to have changed, due to the background-color
applied to the th
cells “covering up” the background-color
on the row itself).
nth-child
is a pseudo-selector. The expression in the parentheses immediately after it may take the keyword odd
, even
or an expression. For example, tr:nth-child(2n)
, meaning 2 × n
, would be equivalent to tr:nth-child(even). n
increments naturally, from 1 to 2 to 3 to 4 and so on, so the expression would yield:
Multiplier | n | Result |
---|---|---|
2 | 1 | 2 (i.e. style applied to second row) |
2 | 2 | 4 (fourth row styled) |
2 | 3 | 6 (sixth row styled) |
… and so on. You can make the expression more complex: tr:nth-child(10n-9)
would mean:
Multiplier | n | Result | |
---|---|---|---|
10 | 1 | - 9 | 1 (i.e. style applied to first row) |
10 | 2 | - 9 | 11 (eleventh row styled) |
10 | 3 | - 9 | 21 (twenty-first row styled) |
…and so on.
This can be applied to many other kinds of markup, not just tables. If you wanted to make every second list item in an unordered list with an id
of example
appear bold, you could write the following declaration:
ol#example li:nth-child(even) {
font-weight: bolder;
}
If you wanted to have every odd <div>
with a class of product-example
used on a page to have a black border (assuming that you have an earlier rule applying a blue border to <div>
elements) you could write:
div.product-example:nth-child(odd) {
border-color: black;
}
Finishing Touches
There are a few features we could add to the table to enhance its usability. One would be to make it very clear which concert date the user was interested in by highlighting the row that the cursor is over:
tr:hover {
background-color: #f90;
}
Most web developers assume that the :hover
pseudo selector can only be applied to links, but in reality the W3C specification allows :hover
to be applied to almost any element.
Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.