meyerweb.com

Skip to: site navigation/presentation
Skip to: Thoughts From Eric

The Adjacent-Sibling Selector

Web Review
July 2000

As previous articles in this series have shown, CSS has some interesting new selectors. Between the universal selector and the child selector, it's possible to construct selectors which would be next to impossible using traditional CSS1-style selectors.

Well, we're continuing the trend in this installment. This time, though, we'll be talking about something which is almost literally impossible to do with contextual selectors. With the adjacent-sibling selector, you can apply styles to elements based on the elements which immediately precede them in the document.

How It Works

The syntax is straightforward enough: just two or more selectors separated by a plus (+) symbol. The simplest construction is two elements with a plus between them. The selected element will be the one immediately following the plus symbol, but only when following the element listed before the plus. For example:

H1 + P {margin-top: 0;}

The rule will apply to all paragraphs which immediately follow an H1 element, and which share the same parent. That's where the name comes from: The elements must have the same parent element, which makes them siblings, and they must be adjacent to each other in the document tree.

Let's look at this in a little more detail. Examine the tree view in Figure 1 for a moment.


Figure 1: Finding adjacent siblings with a document tree.

One example of adjacent siblings is the EM and STRONG elements within the paragraph. In fact, the only place in Figure 1 where elements don't have adjacent siblings is where they don't have any siblings at all, such as the A element; and in the unordered list with three LI children. There the first and third LI elements are not adjacent. The first and second are adjacent, as are the second and third, but the first and third are separated by the second and thus not adjacent.

So let's say that we want STRONG text to be green, but only when it follows EM text. The rule for this is:

EM + STRONG {color: green;}

Referring back to Figure 1, we can see that the STRONG element which is part of the paragraph will be green, but the STRONG which is part of the LI will not. Note that this is true despite the fact that there may be text within the paragraph which is found between the EM and STRONG elements. For example:

<p> This paragraph contains some
<em>emphasized text</em> and, after
that, we find some <strong>strongly emphasized 
text which is also green</strong> despite the 
intervening text.</p>

The text between the elements does not affect the operation of the selector. This is true even with block-level elements. Consider:


<div>
<h3>Hey, an H3 element</h3>
Here's some text which is part of the DIV, 
and not contained in a child of it. 
<p>Here's a paragraph which is short</p>
</div>

We can make the paragraph gray with the following rule:

H3 + P {color: gray;}

Remember that the adjacent selector only cares about elements, and where they fit into the document structure. That's why the text is effectively ignored. (Technically, it's part of the DIV and so lives "one level up" in the document tree, as part of the parent DIV.)

Combining With Other Selectors

Of course, the previous example will actually select any paragraph which follows an H3, no matter where in the document that happens. If we only want to pick those paragraphs that follow H3 level headers which are contained within a division (DIV), then we would write:

DIV H3 + P {color: gray;}

Suppose we want to narrow it down further: we only want this grayness to happen when the H3 and P elements are the children of a DIV (as opposed to descendants of any level). In this case, we write:

DIV > H3 + P {color: gray;}

Now, let's make things a little more general. Suppose we want any element which follows an H3 which is the child of a DIV to be colored gray—any element at all. You know where this is going, right?

DIV > H3 + * {color: gray;}

We can turn this around, too. We might want to apply styles to any element which is the descendant of a DIV with a class of aside which immediately follows a table. In addition, any hyperlinks which are found within such a DIV need to be dark gray and underlined. Thus:

TABLE + DIV.aside * {color: gray;}
TABLE + DIV.aside A:link {color: #444; text-decoration: underline;}

If you're still a little unsure of how these work, try taking a moderately complex document of your own and trying to construct selectors which will exactly address a given element using all the various CSS2 selectors we've used here. A little practice will go a long way toward getting comfortable with these selectors. (For a guide to which browsers will help you in this practice, see the "Browser Support" section near the end of the article.)

Interesting Uses

Okay, it's all well and good that we can do this sort of thing, but what's the big deal? There are hundreds of answers, but here are a few which occurred to me as I wrote the article.

A common print effect is to have the first paragraph of an article be italicized, or boldfaced, or larger—at any rate, different in some way from the rest of the article. Assuming that the article's title is an H1 element, then all we need is something like this:

H1 + P {font-style: italic; font-size: 150%;}

This further assumes that no other H1 elements will occur in the article, or if they do, that none will be followed by a paragraph. If you're already classing the H1 to mark it as the article's title, though, then you can turn that to your advantage:

H1.title + P {font-style: italic; font-size: 150%;}

Here's another possibility. You can change the style of every item in a list except the first one. For example, let's say you want the first item in a list to be normally styled, and the following ones to be gray and slightly smaller. Here's the rule:

LI + LI {color: gray; font-size: 90%;}

The first LI in a list won't be selected because it doesn't immediately follow an LI element, but all the rest do.

How about closing up the distance between headings and the following elements? Authors are always trying to do this with classes and other tricks, but with the adjacent-sibling selector it becomes very easy. Try this out in a CSS2-aware browser:

H1, H2, H3 {margin-bottom: 0.125em;}
H1 + *, H2 + *, H3 + * {margin-top: 0.125em;}

Ta-da! The usual amount of margin space between headings and whatever follows them is closed up to a mere eighth of an em. You can vary that amount as you like, of course. This can be adapted in any number of ways—you could pull lists up closer to paragraphs by using P + UL, increase the separation between tables which immediately follow one another, and any number of other things.

Browser Support

Adjacent-sibling selectors are supported in Internet Explorer 5.x Macintosh. They are also supported in the Netscape 6 preview release 1 for all the myriad platforms for which it's available, and in preview release 3 of Opera 4 for Windows. There are bugs in the handling of adjacent-sibling selectors in IE5 for Windows, and Opera 3 for Windows.

Still More to Come

In many ways, the adjacent-sibling selector is the coolest of the new CSS2 selectors. Thanks to its addition to CSS, it's very easy to select for certain circumstances which many authors want to address, like closing up the space after headings, but until now have been forced to use classes or other tricks to handle. When combined with things like the universal selector, a vast array of possibilities open up.

In the next article, we'll take a look at two new pseudo-classes. One of them can be useful in multiple-language documents, and the other can be very useful in any document at all.

Identity Archipelago

Projects Elsewhere

Redesign Watch

archive

Extras