demosthenes.info

A blog by Dudley Storey on , , , , , , and anything else that strikes his fancy.

featured articles

popular favourites

Striped Table Screenshot

Counting with CSS3: Even, Odd, Intervals & Multiples

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:

  1. <table>
  2. <caption>The Tramps Tour Dates - Subject To Change</caption>
  3. <tr>
  4. <th>Date</th><th>Location</th>
  5. <th>Time</th><th>Book</th>
  6. </tr>
  7. <tr>
  8. <td>November 22</td><td>The Flying Barstool, NB</td>
  9. ;<td>7.00 pm</td><td><input type="button" value="Buy Tickets" /></td>
  10. </tr>
  11. <tr>
  12. <td>November 28</td><td>The Rusty Schooner, NS</td>
  13. <td>9.00 pm</td> <td><input type="button" value="Buy Tickets" /></td>
  14. </tr>
  15. <tr>
  16. <td>December 24</td><td>The Empty Jug, NS</td>
  17. ;<td>8.00 pm</td> <td><input type="button" value="Buy Tickets" /></td>
  18. </tr>
  19. <tr>
  20. <td>December 25</td><td>Lobstercatch Xmas Party</td>
  21. <td>8.00 pm</td> <td><input type="button" value="Buy Tickets" /></td>
  22. </tr>
  23. </table>

This is an entirely appropriate use of a table: the concert dates are truly tabular data, with relevant, related information in each row. (Note that we can add the generic form input value “button” to the table without making the page invalid. By themselves, however, the buttons do nothing: in order to process the ordering of a ticket on the website we would typically wrap the table in a <form> and <fieldset> tag.)

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:

  1. table { border-collapse: collapse;
  2. font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif; }
  3. caption { font-size: larger; margin-bottom: 1em; }
  4. th { background: #000; colour: white; }
  5. 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 colour. In CSS Level 1 we would 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:

  1. tr. highlight { background-color: #cc9; }

We would then modify our HTML to match the affected rows to the CSS:

  1. <table>
  2. <caption>The Tramps Tour Dates - Subject To Change</caption>
  3. <tr>
  4. <th>Date</th><th>Location</th>
  5. <th>Time</th><th>Book</th>
  6. </tr>
  7. <tr>
  8. <td>November 22</td><td>The Flying Barstool, NB</td>
  9. <td>7.00 pm</td><td><input type="button" value="Buy Tickets" /></td>
  10. </tr>
  11. <tr class=“highlight”>
  12. <td>November 28</td><td>The Rusty Schooner, NS</td>
  13. <td>9.00 pm</td> <td><input type="button" value="Buy Tickets" /></td>
  14. </tr>
  15. <tr>
  16. <td>December 24</td><td>The Empty Jug, NS</td>
  17. <td>8.00 pm</td> <td><input type="button" value="Buy Tickets" /></td>
  18. </tr>
  19. <tr class=“highlight”>
  20. <td>December 25</td><td>Lobstercatch Xmas Party</td>
  21. <td>8.00 pm</td> <td><input type="button" value="Buy Tickets" /></td>
  22. </tr>
  23. </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 scream 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 <tr>’s. This disorders the table rows, forcing you to move and re-apply classes every time a change is made. Your attempt to make the tour schedule more legible has increased your workload exponentially.

Traditionally, this is where JavaScript or some other language has to be brought in, as CSS1 and 2 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 inside this div 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 CSS3, 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:

  1. 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:

Result of 2n
MultipliernResult
212 (i.e. style applied to second row)
224 (fourth row styled)
236 (sixth row styled)

… and so on. You can make the expression more complex: tr:nth-child(10n-9) would mean:

Result of 10n-9
MultipliernResult
101- 91 (i.e. style applied to first row)
102- 911 (eleventh row styled)
103- 921 (twenty-first row styled)

…and so on. More details and expressions are available at the W3C CSS3 selectors page; there's also a very useful nth-child visual calculator.

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:

  1. 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 divs) you could write:

  1. 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:

  1. 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.

We could also make it clear that clicking on the “Buy Tickets” button will start an order process by changing the cursor when it is over it. In the context of this page, we could use the following:

web developer guide

featured comment

by JoelB in Goodbye, JQuery Validation: HTML5 Form Errors With CSS3

what i'm reading

A Storm of Swords: A Song of Ice and Fire: Book Three
A Storm of Swords: A Song of Ice and Fire: Book Three

what i'm watching

Californication: The Third Season
Californication: The Third Season

what i'm playing

Mass Effect 3 Collector's Edition
Mass Effect 3 Collector's Edition

what i'm hearing

Dub FX
Dub FX

blogs

podcasts

no ads ever

This blog is free of advertising, and always will be.

creative commons licensed

The content of this blog is free to use in whatever way you wish under the Creative Commons license.