Categories:

Overview of CSS3 Structural pseudo-classes

Date posted: October 2nd, 2008

CSS's goal of "separating style from content" relies heavily on its ability to reach that content first. Well, in CSS3, that mission is bolstered by the addition of Structural pseudo-classes. These selectors let you select child elements of a parent based on a variety of generic criteria, such as the 3rd child, even/odd child elements, the nth child within each group of children of a certain type (ie: LI) within the parent, and more. A little overwhelming, yes, but it's sure better than underwhelming, right?

Meanwhile back in the real world, CSS3 Structural pseudo-classes are supported in Firefox 3.1+, IE8+ (only in IE8 standards mode), and Safari 3.1+ to  various degrees, so they are usable right away.

CSS3 Structural pseudo-classes

Here are the new structural pseudo-classes of CSS3, with first-child of CSS2 thrown in to complete the set:

CSS Structural pseudo-classes
Method Description
E:root Selects the element E that is the root of the document, which in a HTML document is the HTML element.
E:first-child Selects the element E that is the first child of its parent.
E:last-child Selects the element E that is the last child of its parent.
E:nth-child(n) Selects the element E that is the nth child of its parent. Example:

li:nth-child(1) /*selects the first child of a parent if it's a LI element.*/

Do not confuse the above example with "selecting the first LI element of a parent." li:nth-child(1) selects the first child of a parent if it happens to be a LI, not the first LI of a parent. For the later, you'll need to use E:nth-of-type(n) below.

E:nth-last-child(n) Selects the element E that is the nth child of its parent counting from the last, or reverse of :nth-child(n). Example:

li:nth-last-child(1) /*selects the last child of a parent if it's a LI element.*/

Same precautionary note as for E:nth-child(n) above.

E:nth-of-type(n) Selects the element E that is the nth sibling amongst its peers of the same type within their parent. Similar to E:nth-child(n) above, though instead of simply selecting the nth element of some parent, you are now selecting the nth element of a particular type. Example:

ul li:nth-of-type(1) /*selects the first LI of every UL on the page, including nested ULs*/
p:nth-of-type(odd) /*selects all ODD paragraphs on the page*/

E:nth-last-of-type(n) Selects the element E that is the nth sibling amongst its peers of the same type, counting from the last, on the page. Example:

option:nth-last-of-type(2) /*selects the 2nd to last option within each SELECT*/
option:nth-last-of-type(-n+3) /*selects the last 3 options within each SELECT*/

E:first-of-type Selects the element E that is the first sibling of its type in a list of children of its parent element. Example:

p>quote:first-of-type /*selects the first quote element within each paragraph*/

E:last-of-type Selects the element E that is the last sibling of its type in a list of children of its parent element. Example:

tr>td:last-of-type /*selects the last cell of each table row*/

E:only-of-type Selects the element E that has a parent element and whose parent element has no other children element of the same type, In other words, the selected element is the only one of its kind within the parent. Example:

div>p:only-of-type /*selects a paragraph that is the only paragraph within a DIV*/
input[type="radio"]:only-of-type /*selects a radio button if it's the only one inside a form*/

E:only-child Selects the element E that is the one and only child within its parent regardless of type. Example:

div>p:only-child /*selects a paragraph that is the only element in a DIV*/

   
E:empty Selects the element E that has no children at all (including text nodes). HTML comments do not affect whether the element is empty or not. Take a look at the following:

Empty Elements:

<p></p>
<p><!--Empty paragraph</p>

Non Empty:

<p>Welcome to JavaScript Kit</p>
<p><b></b></p>

E:target Selects the element E referenced by the fragment identifier (if any) of the page's URL. This lets you dynamically select the jumped-to anchor from an HTML anchor on the page. For example, given the below URL:

http://mysite.com/page.htm#whatsnew

You could, for example, dynamically add an arrow image next to the anchored content whenever the browser jumps to it:

<style type="text/css">

*:target:before{
content: url(arrow.gif) /*adds an image in front of the current active HTML anchor*/
}
</style>

<body>

<a href="#whatsnew">Jump to what's new</a>

<a name="whatsnew"></a><b>New content 1...</b>

</body>

Also see: Added Generated Content in CSS2.

:not(s) Called the negation pseudo-class, ":not(s)" selects elements that are not of the type supplied by the argument, where the argument is a simple selector (excluding the negation pseudo-class itself and pseudo-elements). Example:

:not(p) /*selects non P elements on the page*/
input:not([type="submit"]) /*selects INPUT elements that are not the submit button.*/
option:not([selected="selected"]) /*selects non default selected OPTIONs*/

   
E:enabled Selects the element E that is enabled, most commonly form elements. Example:

input[type="text"]:enabled /* /*selects text boxes that are enabled (by default they all are)*/
input [type="submit"]:enabled /*selects submit buttons that are enabled (by default they all are)*/

E:disabled Selects the element E that is disabled, most commonly form elements. Example:

input[type="text"]:disabled /* /*selects text boxes that are disabled*/
input[type="submit"]:disabled /*selects submit buttons that are disabled*/

E:checked Selects the element E that is currently checked (most commonly radio or checkboxes). "Checked" is defined when a radio button or checkbox is either explicitly selected by the user, or checked by default using the "checked" and "selected" attributes, respectively. Example:

input:checked /*selects radio or checkbox elements that are currently checked*/
input[type="radio"]:checked /*selects radio buttons that are currently checked*/

Formula for selecting the desired elements using Structural pseudo-classes

Some of the Structural pseudo-classes such as "E:nth-child(n)" and "E:nth-of-type(n)" above expect an argument "n" to limit the items returned. You saw a few possible values, such as an integer, "odd", and "-n+3". It's high time to explain just what the rules are now. To start, the argument "n" is actually a simplification of the formula:

an+b

where:

  • "n" is either just the variable itself or an integer (0 or greater) that sets a base value.
  • "a" is an optional integer (0 or greater) that if defined creates an amplification of "n", such as "2n".
  • "b" is an optional integer (0 or greater) that if defined adds an additional offset to "an", such as "3n+1".

When you leave out the optional "a" and "b" arguments, the basic concept is fairly simple- the integer "n" corresponds to the position of the element within the returned data you want to select, where 1=1st element, 2=2nd element etc. For example:

ul li:nth-of-type(1) /*selects the first LI of a UL*/
p *:nth-child(3) /*selects the 3rd child in a Paragraph*/
option:nth-of-type(even) /*selects even OPTIONs in a SELECT menu*/
option:nth-of-type(odd) /*selects odd OPTIONs in a SELECT menu*/

Notice the last two examples using the keywords "odd" and "even", which are valid values for "n". With those keywords, you can filter down the result using the two given patterns. But what if you wanted something more intricate, such as "every 3rd element", or "all elements starting after the 10th"? Using keywords to specify such patterns isn't feasible, and hence the support for the formula an+b was implemented.

How "an+b" operates is actually pretty simple, once you're able to locate that light switch in your brain and turn it on that is. Think of "a" as a way to amplify the value of "n" a certain number of times if needed, and "b" as a way to affect the initial offset of the pattern. To get things rolling, take a look at the below examples, which are equivalent to one another:

option:nth-of-type(even) /*even OPTIONs in a SELECT*/
option:nth-of-type(2n) /*same as proceeding*/

option:nth-of-type(odd) /*odd OPTIONs in a SELECT*/
option:nth-of-type(2n+1) /*same as proceeding*/

So why is "2n" equivalent to even, and "2n+1" equivalent to odd? You may already have fuzzy idea why this is so, but the real key is this: Think of "n" as a sequentially running number that always starts at 0 and goes all the way up to infinity, so 0,1,2,3, etc. To construct the desired formula, for say, even numbers, your goal is to define "an+b" as such so that the output is even numbers, or 0,2,4,6,8,etc given that n=0,1,2,3,etc. With that understanding, it becomes clear "2n" is the formula you want to use to derive such a result:

n= 0, 1, 2, 3, 4, 5, etc
2n= 0, 2, 4, 6, 8, etc

Lets try out some more examples:

p:nth-of-type(n+1) /*all P elements after the 1st one*/
p:nth-of-type(n+5) /*all P elements after the 5th one*/
p:nth-of-type(3n+2) /*selects the 2nd, 5th, 8th, etc... P elements*/

tr:nth-of-type(-n+5) /*First 5 rows of a table*/
tr:nth-last-child(-n+5) /*Last 5 rows of a table*/

"Wait, what's going on in the last two examples?". Well, glad you asked! Lets think this through methodically shall we:

n= 0, 1, 2, 3, 4, 5, 6, 7, etc
-n+5= 5, 4, 3, 2, 1, 0, -1, etc

Remember that little trick mentioned earlier to always think of "n" as a running integer starting from 0? It definitely comes in handy here as it in showing why the expression "-n+5" is what we want to retrieve the first five (or last five) elements from the result.  "-n+5" returns the positive integers from 5 down to 1 before reaching 0 and into negative territory. Any output that's 0 or negative is meaningless when it comes to specifying which element to select, hence only the elements from 5 down to 1 are selected, yielding what we want, whether it's the first five rows of a table, or the last five instead.

Alternating the background color of rows within a table

Lets see a concrete demonstration of structural pseudo-classes now. The below alternates the background color of rows in a table, plus makes the font inside the first cell of each row bold:

CSS:

<style type="text/css">

#sampletable th{
background: silver;
}

#sampletable tr:nth-of-type(odd){ /*odd rows*/
background: lightyellow;
}

#sampletable tr:nth-of-type(even){ /*even rows*/
background: lightblue;
}

#sampletable tr>td:first-of-type{ /*first cell of each row*/
font-weight: bold;
}

</style>

HTML:

<table id="sampletable" border="1" width="80%">
<tr>
<th>Name</th>
<th>Age</th>
</tr>
<tr>
<td>George</td>
<td>30</td>
</tr>
<tr>
<td>Sarah</td>
<td>26</td>
</tr>
<tr>
<td>David</td>
<td>42</td>
</tr>
<tr>
<td>Collin</td>
<td>32</td>
</tr>
</table>

Demo (requires FF 3.1+, Safari 3.1+, or IE8+):

Name Age
George 30
Sarah 26
David 42
Collin 32

Structural pseudo-classes to select DOM elements

I'm just thinking out loud here, but with all the power structural pseudo-classes, or CSS selectors at large afford us in selecting the elements we want on a page, wouldn't it be nice if that same power could be used to select DOM elements as well? Fortunately this isn't just wishful thinking, as W3C has decided to implement the Selectors API that lets you translate what you select using CSS selectors into DOM elements instead. See "W3C CSS Selectors API" for all the glorious details.