CSS 2.1 selectors, Part 2

This is the second part of a three-part article series that explains the selectors that are available in CSS 2.1. Part 1 is about the more basic stuff like type selectors, class and id selectors, the universal selector, and simple selectors.

In this part I will take a closer look at the more advanced selectors, not all of which are yet fully supported in all major browsers. Support is getting better though, so learning about the selectors that are described in this article is time well spent.


Combinators are used to separate the two or more simple selectors that make up a combined selector. The available combinators are whitespace (any number of tab, space or other whitespace characters), >, and +. What each combinator does is described in the next few sections.

Descendant selectors

A descendant selector is made up of two or more simple selectors separated by whitespace. It matches elements that are descendants of an element that matches the first simple selector. For example, the selector in this rule would match all p elements that are descendants of a div element:

  1. div p { color:#f00; }

Each of the selectors that form a descendant selector can be a simple selector of any form. The selector in the following rule matches all p elements with a class name of info that are contained by an li element that is contained by a div element with the id myid.

  1. div#myid li p.info { color:#f00; }

Descendant selectors allow you to target elements without giving them a class name or an id, which in turn helps keep your markup clean. Let’s assume you have a navigation list consisting of the following markup:

  1. <ul id="nav">
  5. </ul>

To target only those list items and links that are contained within the navigation list you could use the following CSS:

  1. #nav li { display:inline; }
  2. #nav a { font-weight:bold; }

These rules will not match any other list items or links in the document. Compare that to giving a class name to each list item and link, and you’ll realise how much cleaner your markup can become when you use descendant selectors.

Child selectors

A child selector targets an immediate child of a certain element. A child selector consists of two or more selectors separated by a greater than sign, “>”. The parent goes to the left of the “>”, and whitespace is allowed around the combinator.

This rule will affect all strong elements that are children of a div element:

  1. div > strong { color:#f00; }

Only strong elements that are direct descendants of div elements will be targeted by this rule. If there is any other element between the div and the strong elements in the document tree, the selector will not match. In the following example, only “Text one” will be affected by the above rule:

  1. <div>
  2. <strong>Text one</strong>
  3. <p><strong>Text two</strong></p>
  4. </div>

Adjacent sibling selectors

An adjacent sibling selector is made up of two simple selectors separated by a plus sign, “+”. Whitespace is allowed around the adjacent sibling combinator. The selector matches an element which is the next sibling to the first element. The elements must have the same parent and the first element must immediately precede the second element:

  1. p + p { color:#f00; }

If applied to the following example, the above rule will only affect “Paragraph two”:

  1. <div>
  2. <p>Paragraph one</p>
  3. <p>Paragraph two</p>
  4. </div>


I decided to mention grouping at this point, because one common mistake I see people making when they are learning CSS is related to grouping combined selectors.

To apply the same rules to elements matched by several different selectors you can group them into a comma-separated list instead of repeating the declarations for each selector. The mistake many make is to not list the full selectors. Assume that you have the following markup:

  1. <div id="news">
  2. <h3>News</h3>
  3. <ul>
  4. <li>Item 1</li>
  5. <li>Item 2</li>
  6. </ul>
  7. </div>

Now, say you want to apply the same amount of margin to level 3 headings and unordered lists, but only if they are in the div element whose id is “news”. Here is the wrong way:

  1. div#news h3, ul {
  2. margin:0 2em;
  3. }

This will affect both h3 and ul elements in div#news. The problem is that it will target all ul elements in the document, not only those in div#news.

Here is the correct way of grouping the selectors in this case:

  1. div#news h3,
  2. div#news ul {
  3. margin:0 2em;
  4. }

So, when grouping selectors, remember to fully specify each selector.

Attribute selectors

Attribute selectors match elements based on the presence or value of attributes. There are four ways for an attribute selector to match:

Matches elements that have an att attribute, regardless of its value.
Matches elements that have an att attribute with a value of exactly “val”.
Matches elements whose att attribute value is a space-separated list that contains “val”. In this case “val” cannot contain spaces.
Matches elements whose att attribute value is a hyphen-separated list that begins with “val”. The main use for this is to match language subcodes specified by the lang attribute (xml:lang in XHTML), e.g. “en”, “en-us”, “en-gb”, etc.

Some examples then. The selector in the following rule matches all p elements that have a title attribute, regardless of which value it has:

  1. p[title] { color:#f00; }

In the following example, the selector matches all div elements that have a class attribute with the value error:

  1. div[class=error] { color:#f00; }

In order to match all td elements whose headers attribute value contains “col1”, the following selector can be used:

  1. td[headers~=col1] { color:#f00; }

And finally, the selector in the following example matches all p elements whose lang attribute starts with en:

  1. p[lang|=en] { color:#f00; }

Multiple attribute selectors can be used in the same selector. This makes it possible to match against several different attributes for the same element. The following rule would apply to all blockquote elements that have a class attribute whose value is exactly “quote”, and a cite attribute (regardless of its value):

  1. blockquote[class=quote][cite] { color:#f00; }

To be continued…

And that brings us to the end of Part 2. In Part 3, the final part in this series, I will explain the pseudo-class and pseudo-element selectors. While you’re waiting for that you may want to revisit Part 1. Repetition is good for learning.


This article has been translated into the following languages:

Posted on October 10, 2005 in CSS


  1. One thing that is definitely missing from CSS 2.1’s selectors is the ability to select and element that contains another element. Mike Davidson and I came across this one over the weekend: say we want to apply a background color to links, but not links that contain images. I think the most meaningful syntax for this would be something like:


  2. Okay, that didn’t post properly. Sorry about that. My idea was that it would be just like a child selector, but with the “greater than” sign swapped out for a “less than”.

  3. October 10, 2005 by Roger Johansson (Author comment)

    Sorry, my comment preview likes to destroy anything that looks like HTML or character entities. Did you mean something like this:

    a < img


    That would indeed be very useful.

  4. October 10, 2005 by fuska


    1. div[title=error] { color:#f00; }

    Did you mean a title attribute?

  5. October 10, 2005 by Roger Johansson (Author comment)

    Yes. Thanks for catching that error!

  6. Great overview. This is the kind of stuff that’s always a useful read, even if you know all of it already. Just like you said yourself: repetition is good for learning.

    Thanks for a great repetition! ;)

  7. I just had two conflicting thoughts: 1. You are the man! 2. You are a machine! Heh. Go you, and your blasted articles about technical everything. Leave a few for the rest of us to write about, okay!?!? :)

  8. October 11, 2005 by zcorpan

    I discussed “parent selectors” with Anne a while ago, and I also asked about a “previous sibling selector”. His response was that it wasn’t the design of CSS, but there’s a proposal for a :matches() pseudo-class selector which could make it possible.

    a:not(:matches(img)) { background:lime; }

  9. Hi. I’m not sure I understand the [att~=val] and [att|=val] attribute selectors. Say I want any element that has a “lang” attribute equal to French, German or Italian formatted as bold text. Would this be correct?

    *[lang~=”fr de it”] { font-weight: bold }

    Is that what you mean by a “space-separated list” that contains the targeted values? Thanks for the help.

  10. October 12, 2005 by Roger Johansson (Author comment)

    Christian: Not quite. The space-separated list refers to the element’s attribute value:

    *[class~="info"] { font-weight: bold }

    will target any element whose class attribute contains the value “info”, just like the class selector. So it will match this element:

    <p class="info important"></p>
  11. OK, I think I’ve got it now. Thanks again.

  12. The grouping is tricky!! I have realized bout that.

    thanks for the info

  13. February 21, 2006 by Andrea

    Hi, thanks to you I could decrease the list of my css rules! I knew before, that you could group them, but it never worked! Your site is very useful.
