Attribute Selectors Part II

Web Review
August 2000

Attribute selectors are a fascinating new addition to CSS. As we saw last time, you can use them to apply styles to elements based on the presence of a certain attribute, or based on the specific value of a given attribute. That's already a lot of power, but it's just the beginning. As we'll see in this installment, you can start applying styles based on one of a number of attribute values, or even fragments of some types of values.

Picking from a List

Many authors don't realize this, but it is possible to have more than one value in a single class attribute. For example:

<p class="urgent warning">...

Instead of being just one value, this can be seen as the joining of two distinct values: urgent and warning. Given this view, it would be great if we could create styles for each, and then another set of styles which is applied only when the two values appear together.

Although we can accomplish this with relatively ordinary CSS1-style selectors (we'll see how in a moment), we can also do it a little more precisely with attribute selectors. First, let's consider what we want for our styles. Urgent text should be red, we decide, and warning text should be boldfaced. If something is both a warning and urgent, then the font size should be 120% the normal font size. In addition, we want this to be true for all elements, not just paragraphs. Okay, here's what we do:

*[class~="urgent"] {color: red;}
*[class~="warning"] {font-weight: bold;}
*[class="urgent warning"] {font-size: 120%;}

Notice the difference? The first two selectors use a tilde-equal sign combination ( ~= ), whereas the third just has a plain equal sign.

When the equal sign in an attribute selector is preceded by a tilde ( ~ ), that means that the selector will match if the value listed is any one of the space-separated values of the given attribute. So the first rule's selector, *[class~="urgent"] , will match any of the following elements:

<p class="very urgent really">
<table class="urgent">
<ul class="not urgent">
<pre class="not terribly urgent but still worth knowing">

So long as the value urgent appears in the list of values, then the element will match and be colored red. This is true even if urgent is the only value in the list, as we see with the TABLE in the previous example. It's also perfectly happy to match not urgent, so remember to be careful with your class values in order to avoid semantic strangeness.

On the other hand, assume we took out the tilde, so we were left with *[class="urgent"]. Given this selector, only the TABLE would match, because it's the only element whose value exactly matches the value shown in the selector.

Attribute Selectors and Traditional Classes

Now, this may make it seem as though *[class="urgent"] and *.urgent are the same, but in fact they are not. Instead, according to CSS2, *.urgent is equivalent to *[class~="urgent"]. That's right, a traditional class selector will match any one of the values in a list of values. According to the specification, that is; in the real world, many older browsers don't act this way. Of course, those browsers don't seem to support multiple values for a given attribute, so that might explain why they have trouble here.

If you want to use traditional-style class selectors, but want to more precisely match multiple values, you can string them together, like this:

*.urgent.warning {font-size: 120%;}

Remember, though, that this is not an exact match. The preceding rule would match all of the following:

<p class="urgent warning">
<ul class="not an urgent warning">
<pre class="this warning is not at all urgent">

The last element there illustrates another aspect of this kind of selecting: it is not order-sensitive. All that has to happen for a match to occur is that the values in the selector have to appear in an element's attribute in any order. So you get matches for p.red.white.blue from the following:

<p class="red white blue">
<p class="blue red white">
<p class="hail the red white and blue">
<p class="my white and red cat is feeling blue">

The only way to select only <p class="red white blue"> is to use a non-tilde attribute selector, like this:

P[class="red white blue"]

This would not match <p class="blue red white"> because the values are in a different order. If you want to match any element with only those three values, but with the values in any order, and you have to use attribute selectors, then you would need to write:

P[class~="red"][class~="white"][class~="blue"]

That's a lot more typing than p.red.white.blue, obviously. For this particular selection, it really only makes sense to go with attribute selectors if you're writing stylesheets for non-HTML documents, which probably don't have inherent notions of class. For example:

article[author~="Eric"][author~="Meyer"]

This would select author values of Eric Meyer, Eric Meyer, Meyer Eric, and written by Eric Meyer. It would not, however, select either EricMeyer, Eric_Meyer, or even Meyer, EricÑthat comma fouls things up.

Of course, similar possibilities exist with ID attributes and values. More interesting, though, is the ability to select from one of a number of values in attributes other than class and id.

Reaching Other Attributes

Consider a case where you have, in a document, a number of images. Each of these has alt text, as it should, and each one's text says something like "Figure 5". So every image which is also a figure has the word "Figure" in its alt text.

In other words, the word "Figure" is one of a space-separated list of values. So you can select all of the figures like this:

IMG[alt~="Figure"]

Now you can give all your figures a consistent margin, or a border, or float them all to the right, or whatever else you want to do. Assuming, of course, that they all have the appropriate alt text. You don't even have to class the images in order for this to work, which can really cut down on the amount of markup you have to write.

Actually, given common HTML, the uses for this particular feature are largely limited to the class, id, and alt attributes. In an XML-based markup language, though, the possibilities are nearly infinite, and it's in such circumstances that attribute selectors really come in handy.

Still More To Come

So far we've managed to cover attribute selectors in ways which let us make precise selections, and select from one value among many as well. In the next (and final!) installment, we'll look at another way to select a whole range of similar values, as well as ways to combine different attribute selectors to get really specific. Until then, don't forget about the CSS Selectors Chart, which breaks down support for all of the subjects covered in this CSS2 selector series.