Code Crafting Blueprints

Code Crafting Blueprints

Hard-earned webdev knowledge at your fingertips

The Power of Combinators

| Comments

When working with CSS it’s easy to get stuck with just the basic selectors. Yes, you can get by using only those but you will write better and more terse code if you know some of the more advanced ones. In this article I will show you the power of CSS Combinators - A toolkit that lets you combine the basic selectors to create more powerful CSS.

A base HTML

For the sake of explanation I’m going to use a simple HTML structure to illustrate how each selector works. It’s fairly simple and only contains a few basic elements:

A Base HTML
1
2
3
4
5
6
7
8
9
<div id="wrapper">
  <h1>Big headline</h1>
  <p>Paragraph 1</p>
  <p>Paragraph 2</p>
  <div>
      <p>Paragraph 1 inside div</p>
      <p>Paragraph 2 inside div</p>
  </div>
</div>

It’s a simple structure but it will serve well for our purpose.

The DOM

I often find it useful to look at the HTML as a tree structure when trying to understand how different CSS selectors work. In fact, HTML forms a tree structure in the browser called the DOM - which is short for the Document Object Model.

The HTML structure above could be visualized like this:

A basic DOM tree

As you can see some elements are below other elements and some elements sits side by side.

It’s actually like a family tree and the relations between the elements are in fact called things like parent, child, sibling and descendant.

A recap of the most basic CSS selectors

To cover our bases I’m going to do a quick recap of the four most basic selectors. If you’re already familiar with these, feel free to skip ahead to the section about the combinators.

  • The Universal Selector
  • The Element Type Selectors
  • The Class Selectors
  • The ID Selectors

The Universal Selector

The Universal Selector is an asterisk (*) and applies to all elements in the DOM. It’s useful when you want to target several different element types.

The Universal Selector
1
2
3
* {
  margin: 0;
}

This selector would affect all of the elements in the DOM-tree:

The DOM-tree with the Universal Selector applied

An obvious use case for this selector is to create a simple CSS Reset. You can have this selector at the top of the CSS and set all the properites of all HTML elements to default values.

The Element Selector

The Element Selector
1
2
3
p {
  margin: 0 0 1em;
}
Affected elements with the Element Selector applied

The Class Selector

The Class Selector
1
2
3
.first {
    color: #ff0080;
}
Affected elements with the Class Selector applied

The ID Selector

Since there can be only one specific ID in a HTML document, the ID Selector always target a single element. It uses hash (#) and could look like this.

The ID Selector
1
2
3
4
#wrapper {
  width: 980px;
  margin: 0 auto;
}
Affected elements with the ID Selector applied

So far no surprises. You’re probably already familiar with these selectors and with that brief recap out of the way lets move on to more intesting things, namely the CSS Combinators.

The Combinators

The combinators are used in conjunction with the basic selectors. As the name suggest they enable you to combine other selectors in different ways so that you don’t have to have a class or ID on every single element that you want to target.

So why is this good then? Well it serves you well to keep the HTML and CSS as separated as possible. The least amount of extra attributes you have in the HTML the better. It will not only reduce the page size but also make maintenance easier.

To learn more about the benefits of keeping the HTML and CSS as separate as possible, read the article Separation of Concerns https://codecraftingblueprints.com/post//separation-of-concerns which explores the benefits in greater deatail.

Now, lets move on to the combinators.

CSS Combinators

There are four different combinators in CSS and they each bring their own unique benefit:

  • The Descendant Selector
  • The Child Selector
  • The Adjacent Sibling Selector
  • The General Sibling Selector

The Descendant Selector

The Descendant Selector is just two or more selectors after each other, separated by whitespace. If for example you only want to target the <p> elements inside of the <div class="facts"> you could write:

The Descendant Selector
1
2
3
.facts p {
  color: #ff0080;
}

With this code only <p> elements that lives below the element with the class="facts" are targeted.

Affected elements with the Descendant Selector applied

The Child Selector

The Child Selector is a bit more specific than the previous one. It looks almost the same but you add a greater than character (>) between the two selectors. Doing this you make sure that only the elements that sits on the level immediately below the first selector are targeted.

The Child Selector
1
2
3
#wrapper > p {
  color: #ff0080;
}

Now only the <p> elements immediately below <div id="wrapper"> are affected.

Affected elements with the Adjacent Child Selector applied

This is great for dealing with deeply nested structures where you want the styling to only target one level.

A common use case is when styling drop-down menus. These are often constructed with nested lists (<ul>) where you want the lists to have different styling depending on which level they appear.

The Adjacent Sibling Selector

The Adjecent Sibling Selector targets only elements that’s immediately after an element on the same level in the DOM-tree. It’s used by inserting a plus (+) between the first and the second selector.

The Adjacent Sibling Selector
1
2
3
h1 + p {
  color: #ff0080;
}

Now only the <p> that’s directly after the <h1> is affected.

Affected element with the Adjacent Sibling Selector applied

A common use case for the Adjacent Sibling Selector is when you want the first paragraph in an article to have bigger text than the rest of the paragraphs. You could of course add a class such as .first to it but it’s nicer to not have to add extra attributes in the HTML. Instead you could write something like this:

Make the first paragraph in an article stand out more
1
2
3
4
5
6
7
p {
  font-size: 16px;
}
h1 + p {
  font-size: 24px;
  font-weight: bold;
}

The General Sibling Selector

The General Sibling Selector targets elements that are siblings, but unlike the Adjacent Sibling Selector, the targeted element doesn’t have to appear immediately after the first element. It’s constructed using a tilde (~) between the two selectors.

The General Sibling Selector
1
2
3
h1 ~ p {
  color: #ff0080;
}

Now both <p> elements are affected since they’re all on the same level as the <h1>.

Affected elements with the General Sibling Selector applied

Note that the affected elements need to appear after the target element. So if we have the HTML below, only the two <p> elements that appear after the <h1> will be affected.

Targeted Elements need to appear after the parent element
1
2
3
4
<p>Paragraph 0</p><!-- This paragraph is not affected -->
<h1>Big headline</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>

Conclusion

CSS Combinators are great for reducing the amount of extra classes and ID’s in your HTML and they have pretty good browser support too, at least if you target IE7 and above.

All of the combinators except the General Sibling Selector, are part of the CSS 2.1 specification which means that they’ve been around for quite some time. The General Sibling Selector is part of the CSS 3 specification but it still has really good browser support.

I hope that you will find CSS Combinators useful in your future CSS coding!

Can you think of more use cases for the combinators? Let us know in the comments!

Comments