In September and October of 2005 I published a
series of articles that explained the selectors that are available in
CSS 2.1. A quick summary is that most of the selectors described in
those articles can be used now in modern browsers like
Mozilla/Firefox, Safari, and Opera. We just need to wait for Internet
Explorer to catch up before we can start using the full power of CSS 2.1
selectors. The good news is that Internet Explorer will catch up, at least to some extent, with the release of version 7.
If we look a little further ahead, there are even more powerful
selectors waiting to be implemented and used in CSS 3. Many of the CSS 3
selectors have already been implemented in modern browsers, but in
general support is far too patchy for developers to rely on these new
selectors. However, there are cases where they can be used to add nice
forward enhancing features, so I think taking a look at how the new
selectors in CSS 3 work can be useful.
In this article, the specification I am referring to is the Selectors W3C Working Draft 15 December 2005.
The new selectors described in the document will be used by CSS level
3, but may also be used by other languages. If you are reading this
article months, or even years after that date it may be worth checking
if a more recent version is available.
I am not going to explain the basics of how CSS selectors in general
work here. If you need a refresher, a good place to start is CSS 2.1 selectors, Part 1.
First, a quick overview of the selectors that are new in CSS 3:
Selector type | Pattern | Description |
---|---|---|
Substring matching attribute selector | E[att^=”val”] | Matches any E element whose att attribute value begins with “val”. |
Substring matching attribute selector | E[att$=”val”] | Matches any E element whose att attribute value ends with “val”. |
Substring matching attribute selector | E[att*=”val”] | Matches any E element whose att attribute value contains the substring “val”. |
Structural pseudo-class | E:root | Matches the document’s root element. In HTML, the root element is always the HTML element. |
Structural pseudo-class | E:nth-child(n) | Matches any E element that is the n-th child of its parent. |
Structural pseudo-class | E:nth-last-child(n) | Matches any E element that is the n-th child of its parent, counting from the last child. |
Structural pseudo-class | E:nth-of-type(n) | Matches any E element that is the n-th sibling of its type. |
Structural pseudo-class | E:nth-last-of-type(n) | Matches any E element that is the n-th sibling of its type, counting from the last sibling. |
Structural pseudo-class | E:last-child | Matches any E element that is the last child of its parent. |
Structural pseudo-class | E:first-of-type | Matches any E element that is the first sibling of its type. |
Structural pseudo-class | E:last-of-type | Matches any E element that is the last sibling of its type. |
Structural pseudo-class | E:only-child | Matches any E element that is the only child of its parent. |
Structural pseudo-class | E:only-of-type | Matches any E element that is the only sibling of its type. |
Structural pseudo-class | E:empty | Matches any E element that has no children (including text nodes). |
Target pseudo-class | E:target | Matches an E element that is the target of the referring URL. |
UI element states pseudo-class | E:enabled | Matches any user interface element (form control) E that is enabled. |
UI element states pseudo-class | E:disabled | Matches any user interface element (form control) E that is disabled. |
UI element states pseudo-class | E:checked | Matches any user interface element (form control) E that is checked. |
UI element fragments pseudo-element | E::selection | Matches the portion of an element E that is currently selected or highlighted by the user. |
Negation pseudo-class | E:not(s) | Matches any E element that does not match the simple selector s. |
General sibling combinator | E ~ F | Matches any F element that is preceded by an E element. |
If all that doesn’t make much sense right now, don’t worry. Each
selector will be described in more detail in this article, and there are
examples of how each selector can be used.
Substring matching attribute selectors
This whole group of selectors is new, and the selectors in it let developers match substrings in the value of an attribute.
Assume that you have an HTML document that contains the following markup structure:
<div id="nav-primary"></div>
<div id="content-primary"></div>
<div id="content-secondary"></div>
<div id="tertiary-content"></div>
<div id="nav-secondary"></div>
By using the substring matching attribute selectors you can target combinations of these structural parts of the document.
The following rule will set the background colour of all
div
elements whose id
begins with “nav”:div[id^="nav"] { background:#ff0; }
In this case the selector will match
div#nav-primary
and div#nav-secondary
.
To target the
div
elements whose id
ends with “primary”, you could use the following rule:div[id$="primary"] { background:#ff0; }
This time the selector will match
div#nav-primary
and div#content-primary
.
The following rule will apply to all
div
elements whose id
contain the substring “content”:div[id*="content"] { background:#ff0; }
The elements that will be affected by this rule are
div#content-primary
, div#content-secondary
, and div#tertiary-content
.
The substring matching attribute selectors are all fully supported by
the latest versions of Mozilla, Firefox, Flock, Camino, Safari,
OmniWeb, and Opera, so if it wasn’t for Internet Explorer we could start
using them now.
The :target pseudo-class
URLs with fragment identifiers (an octothorpe, “#”, followed by an
anchor name or element id) link to a certain element within the
document. The element being linked to is the target element, and the
:target
pseudo-class makes it possible to style that element. If the current URL has no fragment identifier, the :target
pseudo-class does not match any element.
Assuming the HTML structure mentioned earlier in this document, the following rule would put an outline around
div#content-primary
when the URL contains that fragment identifier:div#content-primary:target { outline:1px solid #300; }
An example of such an URL is
http://www.example.com/index.html#content-primary
.
The
:target
pseudo-class is currently supported by browsers based on Mozilla and Safari.UI element states pseudo-classes
The :enabled and :disabled pseudo-classes
The
:enabled
and :disabled
pseudo-classes
allow developers to specify the appearance of user interface elements
(form controls) that are enabled or disabled, provided that the browser
allows styling of form controls. The following rules will apply
different background colours to single line text inputs depending on
whether they are enabled or disabled:input[type="text"]:enabled { background:#ffc; }
input[type="text"]:disabled { background:#ddd; }
The :checked pseudo-class
The
:checked
pseudo-class allows developers to specify
the appearance of checked radio and checkbox elements. Again, this is
provided that the browser allows styling of form controls. This CSS rule
will apply a green border to checked radio and checkbox elements:input:checked { border:1px solid #090; }
The UI element states pseudo-classes are currently supported by Opera and browsers based on Mozilla.
Note that many web browsers restrict how much the appearance of form
controls can be changed by web developers. More on that subject is
available in my articles Styling form controls and Styling even more form controls.
Structural pseudo-classes
The structural pseudo-classes allow developers to target elements
based on information that is available in the document tree but cannot
be matched by other simple selectors or combinators. The structural
pseudo-classes are very powerful, but unfortunately current browsers
only support a few of them.
The :root pseudo-class
The
:root
pseudo-class targets the document’s root
element. In HTML, the root element is always the HTML element, which
means that the following two rules are the same (well, almost - :root
has a higher specificity than html
)::root { background:#ff0; }
html { background:#ff0; }
The
:root
pseudo-class is currently supported by browsers based on Mozilla and Safari.The :nth-child() pseudo-class
The
:nth-child()
pseudo-class targets an element that
has a certain number of siblings before it in the document tree. This
argument, which is placed within the parentheses, can be a number, a
keyword, or a formula.
A number
b
matches the b
-th child. The following rule applies to all p
elements that are the third child of their parent element:p:nth-child(3) { color:#f00; }
The keywords
odd
and even
can be used to match child elements whose index is odd or even. The
index of an element’s first child is 1, so this rule will match any p
element that is the first, third, fifth, and so on, child of its parent element:p:nth-child(odd) { color:#f00; }
The following rule matches
p
elements that are the second, fourth, sixth, and so on, child of their parent element:p:nth-child(even) { color:#f00; }
The formula
an + b
can be used to create more complex repeating patterns. In the formula, a
represents a cycle size, n
is a counter starting at 0, and b
represents an offset value. All values are integers. Understanding how
this works is easier when you look at a few code examples, so let’s do
that.
The following rules will match all
p
elements whose index is a multiple of 3. In the first rule, b
is zero and could have been omitted, as in the second rule:p:nth-child(3n+0) { color:#f00; }
p:nth-child(3n) { color:#f00; }
The offset value can be used to define at which child a repeating
rule starts to apply. If you have a data table with 20 rows and want
every odd row after the tenth row to have a different background colour,
you can use this rule:
tr:nth-child(2n+11) { background:#ff0; }
Since
n
starts at 0, the first tr
element to be affected is the 11th. Next is the 13th, then the 15th, and so on.
More details are available in the :nth-child() pseudo-class section of the CSS 3 Selectors specification.
So, what about browser support for this very useful selector? Not
good at all. As far as I can tell, no browser supports this or any of
the other “nth” selectors. Please correct me if you know otherwise.
The :nth-last-child() pseudo-class
The
:nth-last-child()
pseudo-class works just like the :nth-child()
pseudo-class, except that it targets an element that has a certain number of siblings after
it in the document tree. In other words, it starts counting from the
last child instead of the first, and counts backwards. The following
rule will match the second-to-last tr
element of a table:tr:nth-last-child(2) { background:#ff0; }
The
:nth-last-child()
pseudo-class is currently not supported by any browsers.The :nth-of-type() pseudo-class
The
:nth-of-type()
pseudo-class works exactly like the :nth-child()
pseudo-class, but only counts those elements that are of the same type
as the element the rule is applied to. This rule will match every p
element that is the third p
element of its parent:p:nth-of-type(3) { background:#ff0; }
This can be useful if you want to make sure that you are really targeting the third
p
element. At first you might think that you could just as well use the nth-child
pseudo-class, but :nth-child(3)
will take all sibling elements into account, so the result will be different unless all p
elements only have siblings that are also p
elements.
The
:nth-of-type()
pseudo-class is currently not supported by any browsers.The :nth-last-of-type() pseudo-class
The
:nth-last-of-type()
pseudo-class targets an element that has a certain number of siblings of the same element type after it in the document tree. Just like the :nth-last-child()
pseudo-class, it starts counting from the last child instead of the
first, and counts backwards. The following rule will match each
second-to-last sibling of type p
:p:nth-last-of-type(2) { background:#ff0; }
The
:nth-last-of-type()
pseudo-class is currently not supported by any browsers.The :last-child pseudo-class
The
:last-child
pseudo-class targets an element that is the last child of its parent element. It is the same as :nth-last-child(1)
. This rule will match all p
elements that are the last child of their parent element:p:last-child { background:#ff0; }
The
:last-child
pseudo-class works in browsers based on
Mozilla. It is not supported by Opera and is buggy in Safari (the above
rule matches all p
elements in the document). Surprisingly
it works as expected in OmniWeb (tested in version 5.1.1), despite that
browser being based on Safari. That could be caused by a regression in
the latest version of Apple WebKit, since OmniWeb is usually built on a
slightly older version of WebKit than what Safari is using.The :first-of-type pseudo-class
The
:first-of-type
pseudo-class targets an element that is the first sibling of its type. it is the same as :nth-of-type(1)
.p:first-of-type { background:#ff0; }
The
:first-of-type
pseudo-class is currently not supported by any browsers.The :last-of-type pseudo-class
The
:last-of-type
pseudo-class targets an element that is the last sibling of its type. it is the same as :nth-last-of-type(1)
.p:last-of-type { background:#ff0; }
The
:last-of-type
pseudo-class is currently not supported by any browsers.The :only-child pseudo-class
The
:only-child
pseudo-class targets an element whose
parent element has no other element children. It is the same (but with a
lower specificity) as :first-child:last-child
or :nth-child(1):nth-last-child(1)
.p:only-child { background:#ff0; }
The
:only-child
pseudo-class works in browsers based on Mozilla. Safari seems to interpret it as :first-child
(the above rule matches all p
elements in the document that are the first child of their parent element).The :only-of-type pseudo-class
The
:only-of-type
pseudo-class targets an element whose
parent element has no other children of the same element type. It is the
same (but with a lower specificity) as :first-of-type:last-of-type
or :nth-of-type(1):nth-last-of-type(1)
.p:only-of-type { background:#ff0; }
The
:only-of-type
pseudo-class is currently not supported by any browsers.The :empty pseudo-class
The
:empty
pseudo-class targets an element that has no
children. That includes text nodes, so of the following elements, only
the first is empty:<p></p>
<p>Text</p>
<p><em></em></p>
The following CSS rule will match that first element:
p:empty { background:#ff0; }
The
:empty
pseudo-class is currently supported by
browsers based on Mozilla. Safari erroneously applies the rule to all
elements of the given element type.The negation pseudo-class
The negation pseudo-class, written
:not(s)
, takes a
simple selector as an argument. It targets elements that are not
targeted by the simple selector. For example, the following CSS will
target any element that is not a p
element::not(p) { border:1px solid #ccc; }
The negation pseudo-class currently works in browsers based on Mozilla and Safari.
The ::selection pseudo-element
The
::selection
pseudo-element matches the portion of an
element that is currently selected or highlighted by the user. One
possible use for this would be to control the appearance of selected
text.
Only a few CSS properties apply to
::selection
pseudo-elements: color
, background
, cursor
, and outline
.
The following rule will make the foreground colour of a selection red:
::selection { color:#f00; }
The
::selection
pseudo-element currently only works in
browsers based on Safari. The behaviour is a bit unpredictable, so the
Safari implementation needs a bit of improvement. Mozilla based browsers
support it if you use a -moz- prefix: ::-moz-selection
. The prefix will probably be removed eventually.The General sibling combinator
The general sibling combinator consists of two simple selectors
separated by a “tilde” (~) character. It matches occurrences of elements
matched by the second simple selector that are preceded by an element
matched by the first simple selector. Both elements must have the same
parent, but the second element does not have to be immediately preceded
by the first element. This CSS rule will target
ul
elements that are preceded by a p
element with the same parent:p ~ ul { background:#ff0; }
The general sibling combinator is currently supported by Opera and browsers based on Mozilla.
Better browser support wanted
Some CSS 3 selectors are widely supported. Unfortunately, some of the
most useful selectors are either not supported at all or have very
limited support in current browsers. That makes many of the selectors
described in this article more or less useless on today’s web. Don’t be
afraid to experiment though. You can still use those that are supported to provide forward enhancement to advanced browsers.
So, which browser do you think will be the first to support most or all CSS 3 selectors? Safari, Firefox, or Opera? Or maybe even Internet Explorer?