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:
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." |
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-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*/ |
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*/ |
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*/ |
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> Non Empty: <p>Welcome to JavaScript Kit</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"> 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*/ |
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)*/ |
E:disabled | Selects the element E that is disabled, most commonly
form elements. Example: input[type="text"]:disabled
/* /*selects text boxes 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*/ |
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"> |
HTML: <table
id="sampletable" border="1" width="80%"> |
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.