Menu

CSS 3 Selectors

June 18, 2003

Russell Dyer

Although the promise of Cascading Style Sheets (CSS) has been wondrous, the progress has been wanting. As with all W3C standards, there is the lengthy discussion process conducted by the related working group, then the problem of implementation by web browser vendors, and finally the unpredictable period of time for people to update to new versions of their browser. These steps can take a year or two each. To expedite the process, the CSS working group has started grouping related features into modules and releasing them separately, without waiting for completion of all modules. This allows browser vendors to proceed with implementation of CSS updates with the confidence that standards won't be changed by the time the full release of CSS is approved. Ideally the result will be to reduce the process by a year or more.

One CSS module that is expected shortly to move to Recommended (or finished) status by the CSS working group is the Selectors module. Much of it has already been implemented by some of the browser vendors. The vendors seem very keen to expedite the Selectors module since it can improve HTML and XML document design decidedly and for fairly little effort. As CSS grows, more selectors are adopted, although some can be pruned. This article will review all currently approved selectors. It will address the surviving CSS1 and CSS2 selectors in brief and the new CSS3 selectors in more detail.

Basic Definitions

A cascading style sheet is used to set or change the styles of a markup document like a web page. Style sheets contain rules that web browsers follow when loading a web page, an XML document, or any document written in a compatible markup language. A style sheet rule is composed of two basic components: a selector and a declaration. The selector is an identifier of a markup tag. For example, in HTML, p is the selector for <p> (or paragraph) tags. Declarations are the style settings that are to be applied by the browser when it encounters a match in a document for a selector. There are many different declarations that may be made, but it is only selectors that concern us here. So when providing examples, I will focus on selectors and not comment on declarations.

CSS1 Selector Basics

The basic value of a selector is to point to objects or text for which a designer would like to set the style. For instance, suppose one decided to set to navy blue the main heading of each web page on a site containing over a thousand pages. If all of the pages are linked to the same style sheet and each page's main heading is wrapped in <h1> tags, then the task would be simple and quick. It would be a matter of merely inserting into to the style sheet the simple rule below:

h1{color: navy;}

Occasionally designers need to select elements by class or by unique identifiers. Suppose a web page contains several tables and the designer wants the background color of some tables to be silver. Class selectors can be utilized by assigning a class to tables that are to be colored. One would place the first line below in the style sheet and start off silver tables in the document like the second line:

table.special{background-color: silver;}

 <table class='special'>

To change the style for only one element on a site, an identification (ID) can be used:

table#unique{background-color: pink;}

<table ID='unique'>

In CSS1 a few pseudo-class selectors were provided for anchor (<a>) elements: :link, :visited, :active, :hover, and :focus. These can be used to change the style of links depending on their state or on user interaction. Two pseudo-element selectors were also provided: the ::first-letter and the ::first-line selectors are for styling the first letter and the first line contained between selected tags. Pseudo-elements are distinguished by two colons and they can affect partial content, but the declarations of pseudo-class selectors are applied to all of the content.

A powerful function found in CSS1 is the descendant combinator. It allows the designer to select markup elements contained within another specified element. One simply gives the parent element, then a space followed by the descendant. Don't confuse this syntax with that of grouping selectors (e.g., h1, h2, h3{...}). This rule will make the text within <quote> tags italic and green, but only when inside a paragraph:

p quote{font-style: italic; color: green;}

To style elements contained within elements that are contained within ancestor elements, that is, to style grandchildren elements, one would use an asterisk with spaces around it as a combinator:

p * quote{font-style: italic; color: green;}

From these basic selectors, designers have been able to standardize the styles of markup documents and reduce design and production time significantly for the last few years. This has been especially critical for XML developers since XML doesn't have default styling conventions.

CSS2 Selectors

CSS2 offered a universal selector (*) to allow selection of specified elements of all parents. For instance, this rule will select TD cells of all tables:

*.td{font-size: 85%;}

CSS2 expanded selectors to include discerning the values of element attributes. In HTML an image element (<img/>) may contain attributes like src, alt, align, and border. One can check if an image tag has a specified attribute. To set the padding only for images where the border is given, this rule will suffice:

img[border]{margin: 5px;}

You can also check if a value is contained in a space separated list contained in an attribute (e.g., img[alt~="fish"]{...}). Or one can check the full value for an exact match:

img[src="logo.gif"]{border: 0;}

CSS2 also expanded descendant selector possibilities. The pseudo-element selectors ::before and ::after allow one to add content before and after elements found. If one marked up the text of a play with XML tags around the dialog to identify a character's lines (e.g., <romeo>...</romeo>), one could use CSS to add text before and after each set of lines:

romeo::before{content: "Romeo: \"";}

romeo::after{content: "\"";}

Starting in CSS2, designers can now change the style for the first child of an element. To double the size of the first letter of the first paragraph of each section of a document (provided each section is enclosed in <div class='section'></div> tags), try this:

div.section:first-child::first-letter{font-size: 200%;}

Notice that the parent is div; section is the class, not the child. The child is whatever the browser finds first. It will double the size of the first letter of each child's content.

A child combinator selector was added to be able to style the content of elements contained within other specified elements. For example, suppose one wants to set white as the color of hyperlinks inside of div tags for a certain class because they have a dark background. This can be accomplished by using a period to combine div with the class resources and a greater-than sign as a combinator to combine the pair with a, as shown below:

div.resources > a{color: white;}

Also added in CSS2 was the direct adjacent combinator. For example, imagine you want to indent all paragraphs in a document except for ones immediately following a sub-heading (<h2>). The second rule below shows an h2 selector combined with a p selector using a plus sign as the combinator to indicate that the declaration applies only if p immediately follows h2:

p{text-indent: 0.5in;}

h2 + p{text-indent: 0;}

Finally, consider the language selector. The :lang selector is for selecting a document of a specific language:

html:lang(en){color: black;}

xml:lang(en){color: gray;}

Similarly, the hreflang attribute of the link element can be used with the attribute value selector that checks for a value and the same value followed by a hyphen. The following rule will match French (fr) and Belgian French (fr-be):

link[hreflang|="fr"]{font-style: italic;}

Implemented CSS3 Selectors

The Mozilla Project has already implemented several CSS3 selectors and is working on implementing the rest soon. The other vendors aren't far behind, either. Let's look at what Mozilla has implemented first.

Three more attribute selectors were added in CSS3; they allow for substring selection. One matches elements based on the beginning values of named attributes. This could be useful, for instance, if one wants to have different styles for hyperlinks to a sub-domain (e.g., sales.somesite.com), as opposed to the main domain (e.g., www.somesite.com):

a[href^='http://sales.']{color: teal;}

To select elements based on the ending value of attributes, the carat above would be replaced with a dollar sign. The rule below would let users know that they are about to click on a link to a Java Server Page.

a[href$='.jsp']{color: purple;}

CSS3 also provides substring matching irrespective of word boundaries. So if one wants to set the border for images from a particular directory (say, /images/artwork/), this rule will accomplish the task:

img[src*='artwork']{

        border-color: #C3B087 #FFF #FFF #C3B087;}

A few more pseudo-class selectors were added in CSS3. One is the :root selector, which allows designers to point to the root element of a document. In HTML, it would be <html>. Since :root is generic, it allows a designer to select the root element of an XML document without necessarily knowing it's name. To permit scrollbars when needed in a document, this rule would work:

:root{overflow:auto;}

As a complement to the :first-child selector, the :last-child was added. With it one can select the last element named of a parent element. For a magazine site with articles contained in <div class='article'></div> tags, where each has a last paragraph with author information, this rule would change the font for each writer's blurb:

div.article > p:last-child{font-style: italic;}

The parent here is a div with article as the class. Once the browser finds such a div tag, it then looks for matching children (<p> tags here). It then selects the last paragraph and adjusts its style based on the rule's declaration.

A new user interaction pseudo-class selector was added: the :target selector. To draw the user's attention to a span of text when the user clicks on a same-page link, a rule like the first line below would work nicely; the link would look like the second line, the highlighted span like the third:

span.notice:target{font-size: 2em; font-style: bold;}

<a href='#section2'>Section 2</a>

<p id='section2'>...</p>

To complement the direct adjacent combinator introduced in CSS2, the indirect adjacent combinator was included in CSS3. It's used in situations where two specified elements are of the same parent and the second element follows the first, but not immediately afterward. To render text as bold when it's contained within <tt> tags, but not when contained within a table, since there will be many elements for the browser to consider (e.g., <tr> and <td> tags) before it would reach a <tt> tag, a direct adjacent combinator selector wouldn't work. But an indirect adjacent combinator selector could handle this:

tt{font-weight:bold;}

table ~ tt{font-weight: normal;}

Finally, a functional notation for selecting specified elements that fail a test has been created. The negation pseudo-class selector (:not) can be coupled with almost any other selector that has been implemented. To put a border around images that don't have a border specified, use a rule like this:

img:not([border]){border: 1;}

Unimplemented CSS3 Selectors

The selectors that are yet to be implemented are a little more complicated but very powerful; they should be implemented soon by the Mozilla Project. Almost all of them are pseudo-class selectors and are very similar. They will likely all be implemented by Mozilla simultaneously.

Quite often designers create online forms for users to complete. As the user fills out portions of a form, based on the user's answers, some portions won't apply. To dynamically style text or input elements based on whether an element is enabled or disabled, the :enabled and :disabled pseudo-class selectors have been devised:

radio.creditcard_type:disabled{color: gray;}

The :checked and :indeterminant pseudo-class selectors were created for styling based on the status of check boxes and radio buttons. To highlight the text associated with a check box once it has been checked, use this rule:

input:checked{background-color: blue; color: white;}

For check boxes and radio buttons that start off with nothing checked, one could highlight them to draw the user's attention to them as required fields. In the example below, the input element has a class label of required:

input.required:indeterminant{color: red;}

Several descendant selectors await implementation. The :only-child selector is used to select elements that don't have any siblings. For instance, if one wants to indent paragraphs for sections (using XML <section> tags) only when there is more than one paragraph, this rule can solve the problem:

section{text-indent: 0.5in;}

section:only-child{text-indent: 0;}

To style based on the uniqueness of type (i.e., no siblings), the :only-of-type selector is available. A related selector is :empty, used for selecting elements that have no children. Use the first rule below to style unique elements in a section and the second for sections with no children:

section:only-of-type{text-style: italic;}

section:empty{text-style: bold;}

Along the same lines, one can style the first and last descendants based on type. Suppose one has several documents (using <doc> tags) that involve the pattern of an introductory paragraph, several main paragraphs, and a concluding paragraph. One could use this rule to style the first and last paragraphs differently:

doc:first-of-type{text-style: bold;}

doc:last-of-type{text-style: italic;}

Keep in mind when using these pseudo-class selectors in which the element type is not specified, that stray elements might be styled unintentionally.

A very handy and potentially confusing set of selectors that have been added in CSS3 are the nth child pseudo-class selectors. These structural selectors were made for designers to be able create precise rules for climbing a document tree. Here's the basic format of an :nth-child selector:

element:nth-child(an+b){...}

The notation a represents the number of children by which the browser is to group. The notation b is the count of each grouping that is selected. The n is always n and is not replaced with a number. To alternate the background of rows in a table, this rule could be deployed in the future:

tr:nth-child(2n+1){background-color: magenta;}

Notice here that the browser is to organize the rows into pairs and to style the first of each pair. To highlight the first of three rows, then this is how the rule would read:

tr:nth-child(3n+1){background-color: tan;}

One could write a rule for each row of the three:

tr:nth-child(3n+1){background-color: tan;}

tr:nth-child(3n+2){background-color: fuschia;}

tr:nth-child(3n+3){background-color: aqua;}

There is some shorthand that can be used. Rather than 2n+1 for odd and 2n+2 for even, they can be replaced with odd and even:

tr:nth-child(odd){background-color: maroon;}

tr:nth-child(even){background-color: tan;}

Incidentally, if a and b are equal, then b can be left out and the value of a will be the implied value of b. The rules below are equivalent and would be applied to the second row of each pair of rows (i.e., the even rows):

tr:nth-child(2n+2){background-color: indigo;}

tr:nth-child(2n){background-color: indigo;}

For a non-repeating pattern, that is, to style one particular element, then the a notation would be set to zero or just left out. The rules below are equivalent and will style just the eighth row:

tr:nth-child(0n+8){background-color: silver;}

tr:nth-child(n+8){background-color: silver;}

tr:nth-child(8){background-color: silver;}

To have the browser count elements starting from the bottom, use the :nth-last-child selector. The a notation uses a negative to indicate a reverse count. This rule styles the last two rows of a table:

tr:nth-last-child(-n+2){background-color: violet;}

To group on types for elements that are mixed with others, use :nth-of-type selector. For instance, suppose one wants to alternate the alignment of images in a document. These rules would do the trick:

img:nth-of-type(2n+1){float: right;}

img:nth-of-type(2n+2){float: left;}

One can count based on type from the bottom, as well, by using the :nth-last-of-type selector. This rule aligns right only the last two images:

img:nth-last-of-type(-n+2){float: right;}

CSS3 also has a content pseudo-class selector. The :contains selector searches the content of a specified element for a matching substring. This selector is for static media only (i.e., print and not screen display). A rule to style text when printed from a bullet list that contains the word "special," would look like this:

li:contains("special"){text-style: italic;}

Finally, to style text differently from the browser's default style that the user selects, the ::selection pseudo-element selector can be used. Typically, when text is selected by a user for copying, the background color changes to blue or black. This rule will alter the user selected text and limit alterations to text within paragraph tags:

p::selection{

        background-color: maroon; font-size: 200%;}

Since this is a pseudo-element selector, only the selected text will be changed while selected and not the entire paragraph.

Concluding Admissions

Although I have attempted to cover all CSS selectors and give examples of each, there are some twists and aspects that I omitted. For instance, there are many ways to combine selectors to accomplish tasks. A few of the example rules presented utilize two or more selectors, hinting at such potential complexities. I also didn't discuss the possibility of using different namespaces with selectors. There's also the issue of what to do about browsers that aren't CSS3 compatible, but are CSS2 compatible. What will they ignore and with what will they meddle?

If you would like more details, you can review the Web Consortium's documentation on Selectors. If you would like to test your browser for compatibility, try the Web Consortium's test pages. For more on CSS in general, check out CSS: The Definitive Guide by Eric Meyer.