Selector Subtleties

If you have no idea what the title of this blog entry means, chances are that you are not a web developer. Fee free to skip to the next article in this case. We are going to discuss CSS selectors and how they may be used to streamline web development. Traditionally, CSS selectors are used to create logical groups of visual styles which are applied to HTML elements. You could think of selectors as patterns that are matched against a set of tags in a web document. We are considering both CSS 2.1 and CSS 3 selector syntax and functionality, while keeping in mind that the latter is not fully supported by all current browsers. Notably, the Internet Explorer took 10 years from the publication of the CSS 2 specifications in 1998 until the release of IE8 in 2008 with full support for CSS 2.

A proper understanding of selector pattern matching has become more important with the advent of jQuery, the most widely used Javascript library today, that employs CSS selector syntax for DOM element addressing, traversal, and manipulation. Once you have worked with jQuery and its powerful "query by selector" engine, using the conventional DOM document methods, such as getElementById(), feels a bit like operating an unwieldy steam engine. You would not want to go back. It also brings CSS syntax to the world of scripting. Using jQuery effectively, however, requires a fairly good command of some of the more complex CSS selector expressions, understanding the new CSS 3 selectors, and learning a few selector expressions specific to jQuery. But let's start at the beginning with the three most simple and most commonly used CSS selectors:

a { color: orange; }
#orange { color: orange; }
.orange { color: orange; }

The first is a type selector. It assigns the colour orange to the text colour of all anchor tags (links). The second is an id selector. It assigns the colour orange to the HTML element with the id "orange". The third is a class selector. It assigns the colour orange to all elements which have an "orange" class attribute. These three are the bread-and-butter selectors that probably make up 90% of all selectors in CSS style sheets. Tag selectors are typically used for global style settings that apply to a large number of web pages, for example to set the font type for a website. The id selector is typically used in combination with specific layout elements, such as containers or form fields. The class selector is typically used to style elements repetitively in the same manner. These selectors can be combined. For example:

a.orange { color: orange; }

This will set only anchor tags with the CSS class orange to orange text colour. Other anchor tags and other types of tags with the CSS class "orange" set are not affected. Combining multiple selectors in order to match document structure is an important skill that -if mastered- allows you to create efficient and maintainable style sheets. Incidentally, it also makes a good recruiting question for web developer candidates. Can you tell the difference between the following three combinations?

.footer.orange { color: orange; }
.footer .orange { color: orange; }
.footer, .orange { color: orange; }

The first selector matches elements that have two class attributes named "footer" and "orange". The second selector matches elements with the class "orange" that are descendants of an element with the class "footer". The third matches all elements with either the class "footer" or the class "orange". In other words, these selectors express three different types of relationships: logical and (written together), descendant (separated by blanks), and logical or (separated by comma). In geek speak, these are called combinators. There are a few more combinator thingies, although the following are lesser known and used:

#footer > .orange { color: orange; }
#footer + .orange { color: orange; }
#footer ~ .orange { color: orange; }

The angle bracket denotes child relationship. The first example matches all elements with the class "orange" that are children (=immediate descendants) of the element with the id "footer". The plus sign means adjacent sibling. Line 2 matches elements with the class "orange" that are immediately preceded by an element with the id "footer". The tilde denotes a general sibling combinator. The third example matches all elements with the "orange" class that are siblings of the element with the id "footer". These combinators are sometimes handy for formatting lists. The next group of selectors we are going to take a look are attribute selectors. They match elements by attributes and are equally useful for processing HTML and XML documents:

div[class] { color: orange; }
div[class="orange"] { color: orange; }
div[class~="orange"] { color: orange; }
div[class^="orange"] { color: orange; }
div[class$="orange"] { color: orange; }
div[class*="orange"] { color: orange; }

Line 1 selects all div elements that have a class attribute. Line 2 selects all div elements whose class attribute is set to "orange". Another good test question: how is this different from the class selector .orange? Answer: It only selects those elements where the class attribute exactly matches the word "orange". For example, the element <div class="orange fruit"> is not matched. This one is matched by the selector in line 3, however, because ~= matches all div elements where the class attribute contains a whitespace-separated list of words, one of which is "orange". The latter is functionally equivalent to the class selector .orange. Line 4 matches div elements whose class attribute begins with "orange", line 5 matches div elements whose class attribute ends with "orange", and line 6 matches div elements whose class attribute contains the string "orange", such as <div class="all-orange-fruits">.

We go on to the so-called pseudo classes and pseudo elements that provide useful matching techniques for dynamic manipulation in response to user interaction:

div:hover { color: orange; }
input:focus { color: red; }
input:enabled { color: green }
input:disabled { color: grey }
input:checked { color: blue }

Line 1 matches a div element during the time the mouse  pointer is on it (the "rollover" effect). Line 2 matches a form input element that has the focus (the time during which data can be manipulated with the element). Line 3 matches all enabled input elements and line 4 matches all disabled input elements. Line 5 matches a checked form element, such as a radio button or a checkbox. Question: how can you match an unchecked element? The answer is: you need an additional selector. Unchecked elements can be matched by combining the :checked selector with the :not selector, which -strictly speaking- operates like a combinator:

input:not(:checked) { color: yellow; }

You can of course put any valid CSS selector or selector combination into the parentheses of the :not() selector to create more specific and more complex expressions. Finally, there are a number of pseudo class selectors that relate to the DOM tree structure which may be useful for manipulating DOM (in combination with jQuery, for example). The three following lines  match the first, last, and third children of all div elements, respectively:

div:first-child { color: pink }
div:last-child { color: silver }
div:nth-child(3) { color: purple }

Although we have not enumerated all CSS selectors here, the ones we have covered are probably the the most useful and most often used ones. For a complete reference, see the W3C specification for CSS selectors.