CSS Selectors: Just the Tricky Bits

div > p + div[id*=’header’] – If you saw this jumbled mess of selectors in someone’s CSS, could you decipher it?

If not, then you’ll want to read this primer on funky CSS selectors that you should know!

A Flawed Education

Like many self-taught CSS guys, my education was cobbled together from a million different sources. I read articles, followed tutorials and experimented until my head hurt to get to a point where I could bust out a complex layout and achieve the styling that I want without too much trouble.

However, over time I slowly started to realize that I lacked a proper understanding of one of the most basic, essential elements to working with CSS: selectors. Sure, I knew how to target a class, ID or link, but I would often see snippets of code with selectors that seemed completely foreign to me.

After writing these confusing coding practices off for so long I decided to really sit down and learn about CSS selectors. It not only changed the way I code, it has drastically helped me interpret and learn from CSS that others write.

I am certainly not the first to write about CSS selectors and this article will by no means add anything revolutionary to the discussion. But if you’re like me, a self-taught coder with a mottled education, then this could just be the article that clarifies some of your biggest confusions when you stare in wonder at the cryptic code of others.

Meet The Family

Before you learn about complex CSS selectors. You need to understand a few things about HTML hierarchy. You’ve heard these terms tossed around before but it’s time to really think about them and make sure you grasp how they work.

The way we explain and refer to the structure of an HTML document is by using the metaphor of a family tree. There are parents, children, siblings, descendants and ancestors. To see what each means, let’s use the simple code below.

<div id="example1"> <p>Wake Up Neo.</p> <p>Follow the <a href="#">white rabbit</a>.</p> </div> 1 2 3 4 < div id = "example1" > < p > Wake Up Neo . < / p > < p > Follow the < a href = "#" > white rabbit < / a > . < / p > < / div >

Our div contains two paragraph elements and an anchor. All of these are descendants of the div, which occupies the root position in this instance. A child is a direct descendant of a parent. So in the example above, both paragraphs are children of the div but the anchor is not. Instead, it is a child of the second paragraph.

Similarly, if we go back up the tree, the div has a differing relationship to the items. The div is the parent of the paragraph tags but an ancestor of the anchor tag.

The final relationship is that of the siblings, also known as adjacent siblings. Just like in your family, siblings share a parent. So in the example above, the two paragraphs are siblings because the parent of both elements is the div. The anchor has no siblings because it is the paragraph’s only child.

Got That?

The family metaphor can get really confusing, but only if you over-think it. The terminology is fairly intuitive and if you think about the literal meaning of the word, you can pretty much guess what it means in HTML terms.

Just in case you still need a little more clarification, let’s rewrite the example above with content that explains the hierarchy of each element.

<div id="parentOfParagaphs"><!--Also an ancestor of the anchor--> <p>Child of the div above, sibling to the paragraph below.</p> <p>Child of the div above, sibling to the paragraph above and parent to the anchor. <a href="#">Child of the paragraph, sibling of none and descendant of the div.</a>.</p> </div><!--Everything in the div is a descendant to the div--> 1 2 3 4 < div id = "parentOfParagaphs" > < ! -- Also an ancestor of the anchor -- > < p > Child of the div above , sibling to the paragraph below . < / p > < p > Child of the div above , sibling to the paragraph above and parent to the anchor . < a href = "#" > Child of the paragraph , sibling of none and descendant of the div . < / a > . < / p > < / div > < ! -- Everything in the div is a descendant to the div -- >

The Child Selector

Now that we understand how HTML works, we can start our discussion of CSS selectors, which leverage this system. Armed with our knowledge of what an HTML child really is, we can look at the CSS child selector.

The child selector is represented by the greater than symbol: “>”. You’ve seen this in CSS before but may or may not really understand how it works.

#someDiv > p { color: blue; } 1 2 3 #someDiv > p { color : blue ; }

This statement takes the paragraph tags that are children of the div and turns them blue. Note that it only works for the children of that div, not necessarily all of the descendants. Let’s explore this further with the following example.

<div id="example2"> <p>First paragraph <a href="#">with a link</a>.</p> <p>Second paragraph</p> </div> 1 2 3 4 < div id = "example2" > < p > First paragraph < a href = "#" > with a link < / a > . < / p > < p > Second paragraph < / p > < / div >

Let’s say for some horrible reason we wanted to change that link color to yellow. Obviously, we could target the link any number of ways, but we’re trying to learn about the child selector so go with me. Let’s see what happens if we implement this CSS:

div > a { color: yellow; } 1 2 3 div > a { color : yellow ; }

Whoops, that didn’t change anything! That’s because the link is not a child of the div, it’s a child of the paragraph. So if we change that code to target any anchors that are children of paragraphs, we achieve our goal.

p > a { color: yellow; } 1 2 3 p > a { color : yellow ; }

Order

Now that you understand how the child selector works, keep in mind that, as with all CSS, the order makes a big difference. To illustrate this, let’s start with this basic HTML structure:

<div id="example3"> <p>First paragraph</p> <p>Second paragraph</p> <div id="nestedDiv"> <p>Third paragraph</p> </div> </div> 1 2 3 4 5 6 7 8 < div id = "example3" > < p > First paragraph < / p > < p > Second paragraph < / p > < div id = "nestedDiv" > < p > Third paragraph < / p > < / div > < / div >

Now let’s consider the following two CSS snippets. They look similar, but are very different!

#example3 p { color: red } #example3 > p { color: blue } 1 2 3 4 5 6 7 #example3 p { color : red } #example3 > p { color : blue }

#example3 > p { color: blue } #example3 p { color: red } 1 2 3 4 5 6 7 #example3 > p { color : blue } #example3 p { color : red }

The first snippet, makes all of the paragraphs to red, then changes only the children of the root div to blue.

The second snippet does the opposite, it initially targets the first div’s child paragraphs and turns them to blue but then changes all of the paragraphs to red. This of course results in three red paragraphs.

The second example breaks an important rule in CSS: go from general to specific, not the other way around. Since we started with a specific selector setup that targeted only two paragraphs and then moved to a general selector that targeted all of the paragraphs, the second completely overruled the first!

Chaining

If you really want to go nuts, you can chain child selectors together. In the following example, I first targeted any children paragraphs of the example3 div and changed them to blue, then I targeted any paragraphs that were the children of divs that were also children of another div and changed those to red.

<div id="example3"> <p>First paragraph</p> <p>Second paragraph</p> <div id="nestedDiv"> <p>Third paragraph</p> </div> </div> 1 2 3 4 5 6 7 8 < div id = "example3" > < p > First paragraph < / p > < p > Second paragraph < / p > < div id = "nestedDiv" > < p > Third paragraph < / p > < / div > < / div >

#example3 > p { color: blue; } div > div > p { color: red; } 1 2 3 4 5 6 7 #example3 > p { color : blue ; } div > div > p { color : red ; }

The Adjacent Sibling Selector

With child selectors all squared away we need to learn how to target siblings. The adjacent sibling selector is represented by the plus symbol: “+”. With it, we can target any element that is a sibling of any other element. Here’s an example:

<div id="example4"> <p>First paragraph</p> <p>Second paragraph</p> <p>Third paragraph</p> <div id="nestedDiv"> <p>Fourth paragraph</p> </div> </div> 1 2 3 4 5 6 7 8 9 < div id = "example4" > < p > First paragraph < / p > < p > Second paragraph < / p > < p > Third paragraph < / p > < div id = "nestedDiv" > < p > Fourth paragraph < / p > < / div > < / div >

p + p { color: red; } 1 2 3 p + p { color : red ; }

In our CSS, we used the adjacent sibling selector, which changed the second and third paragraph to red. This is very tricky isn’t it? instinctively, we would expect the first paragraph to be red as well. After all, the first paragraph is on the same level of the tree as the next two and has siblings. However, this selector only applies to elements that are preceded by something else. In this instance, only those paragraphs preceded directly by a sibling paragraph will be targeted. The first paragraph in the list is preceded by the div, so it isn’t changed.

With this in mind, the following code would change the background of the nested div to gray because it is indeed preceded by a paragraph.

p + div { background: gray; } 1 2 3 p + div { background : gray ; }

Further Chaining

Just as with child selectors, you can chain adjacent sibling selectors. The system works exactly the same as before. You can also combine the two for an added layer of complication. Here’s where our CSS really starts to look strange.

<div id="example5"> <p>First paragraph</p> <p>Second paragraph</p> <div id="nestedDiv"> <p>Third paragraph</p> <p>Fourth paragraph</p> </div> </div> 1 2 3 4 5 6 7 8 9 < div id = "example5" > < p > First paragraph < / p > < p > Second paragraph < / p > < div id = "nestedDiv" > < p > Third paragraph < / p > < p > Fourth paragraph < / p > < / div > < / div >

div > p + p { color: red; } 1 2 3 div > p + p { color : red ; }

In this example, we targeted paragraphs that were children of divs that were also preceded directly by another paragraph.

The Almighty Asterisk

The final piece of confusing CSS that I want to go over is the worst yet: the asterisk. Did you know that, depending on the context, the asterisk can refer two completely different things?

Universal Selector

The first example of an asterisk in CSS is the one you’re probably the most familiar with: the universal selector. True to its name, this selector targets any and every element on the page. We typically see it used in CSS resets. The following resets the padding and margin on every element.

* {margin: 0; padding: 0;} 1 * { margin : 0 ; padding : 0 ; }

You can take this further and achieve some really interesting results. For instance, the following turns every element inside a div red, this includes items like links that have a default color set to something else and wouldn’t be affected by simply targeting the div.

#someDiv * { color: red; } 1 2 3 #someDiv * { color : red ; }

Further, this snippet targets grandchild paragraphs of the div, but not direct child paragraphs.

div * p { color: red; } 1 2 3 div * p { color : red ; }

You can take this as far as you want, the following targets the great grandchildren of the div. You can see this chaining method used frequently in CSS debugging tricks.

div * * p { color: red; } 1 2 3 div * * p { color : red ; }

Arbitrary Substring Attribute Value Selector

Now with CSS3, the asterisk can also be used to implement the arbitrary substring attribute value selector, you can pull that title out when talking to other developers if you’re trying to look smart.

The following code targets any div with the word “section” in the title. It can be “section3” or “section-Four”, it doesn’t matter, as long as it contains the indicated string, the subsequent styles apply.

div[id*='section'] {color: red;} 1 div [ id* = 'section' ] { color : red ; }

This code would apply to all of the divs in this HTML:

<div id="section-1"> </div> <div id="section-2"> </div> <div id="section-3"> </div> <div id="section-4"> </div> 1 2 3 4 5 6 7 8 9 10 11 12 < div id = "section-1" > < / div > < div id = "section-2" > < / div > < div id = "section-3" > < / div > < div id = "section-4" > < / div >

Attribute Selectors

That last curve ball that I threw you was an example of an attribute selector. In CSS3, attribute selectors are so complicated that they merit their own article! Fortunately for you, we have a piece on that very topic.

CSS Attribute Selectors: How and Why You Should Be Using Them

If you’re a nerd like me, this is a really fascinating topic. It’s amazing to see all of the selector tools that you have at your disposal.

Pseudo Class Selectors

This is yet another selector topic that you should familiarize yourself with, and once again it really does merit a thorough inspection of the topic. As with just about every CSS topic known to man, CSS-Tricks has you covered.

Meet the Pseudo Class Selectors

Conclusion

With CSS, there’s almost always more than one way to target something. In fact, there’s often half a dozen ways. For any of the examples above, you can probably name a quicker way to target what we were shooting for, but as you familiarize yourself with the DOM, these selectors really do start to come in handy.

Even if you never find yourself in need of them, it’s vitally important to understand them because you will see them projects that you collaborate on, frameworks that you download, tutorials that you read and any other place that you regularly find code. Understanding HTML element relationships and their corresponding CSS selectors can only make you a better coder!