Computer scienceFrontendCSSSelectors in Depth

Combinators

10 minutes read

You already know quite a bit about CSS selectors. Tag names, id's, classes, and other attributes can be used to choose elements and add CSS styles to them. The next step would be to look at the elements of a page not separately but as a whole. If a page has a lot of elements with different nesting depth, we can see the dependency between them and use it to create selectors. These selectors are called combinators.

Page example

Let's learn about combinators step by step and with an example. Consider the following HTML page with someone's personal blog:

<article>
    <h3>The 6th of July</h3>
    <p>It was a really nice day! I've spent a lot of time outside and played football with my friends.</p>
    <div style="width: 55px; background-color: mistyrose;">
        <p>I love it!</p>
    </div>
    <p>Here is the list of players of our small team:</p>
    <ul>
        <li>Hanna</li>
        <li>Mike</li>
        <li>Jennifer</li>
    </ul>
</article>
<article>
    <h3>The 4th of July</h3>
    <p>Today was quite boring. It was raining all day, and I was doing my homework from dawn to dusk.</p>
    <p>The assignments I've finished:</p>
    <ul>
        <li>Maths</li>
        <li>Biology</li>
        <li>Physics</li>
    </ul>
</article>

Without any additional styles, the page looks like this:

Simple HTML page witout styles

Let's take that page and use combinators to apply styles to it.

Relationships between elements

First, let's talk about the relationships between elements on HTML pages in general. Just like in our own families, there are parents and children.

Take a look at this piece of code:

<div id="grandparent">
    <div id="parent">
        <div id="child1"></div>
        <div id="child2"></div>
        <p id="child3"></p>
    </div>
</div>

It's simple: if some X element is nested in the Y element, Y is said to be the parent of X, and X is the child of Y. In the code snippet above, #child1, #child2, and #child3 are all children of #parent; meanwhile, #parent is a child of #grandparent.

To talk about these relationships without considering the specific nesting depth, the terms ancestor and descendant are used. If the X element is nested in the Y element, no matter how deep, X is a descendant of Y, and Y is an ancestor of X. In the example, #grandparent and #parent are ancestors of #child1, #child2, and #child3. At the same time, #parent, #child1, #child2, and #child3 are all descendants of #grandparent.

An element can have many children, descendants, and ancestors, but only one parent.

The elements that share the same parent are called siblings. In our code snippet, #child1, #child2, and #child3 are siblings with the parent #parent.

Descendant combinator

Now that we understand the family dynamics, let's return to our blog page example and see what selectors can do.

The A B selector with the space ( ) combinator applies the style to all the B elements that are descendants of the A elements. It's called a descendant combinator.

Let's apply the following CSS code to our page:

article p {
    color: red;
}

article li {
    color: blue;
}

The result will be the following:

Simple HTML page contains red text and blue lists

Note that in this case, B could be placed not directly inside A as it is with "article li" but nested into another element that is itself a descendant of A.

Child combinator

In the case of the A > B selector, it's a little different: the > combinator applies the style only to those B elements that are direct children of the A elements. This is called a child combinator.

Look how the previous example changes with this combinator:

article > p {
    color: red;
}

article > li {
    color: blue;
}

The result is:

Regular HTML page with red text and normal lists

The text in the sentence "I love it." isn't red anymore because this <p> is a direct child of <div>, not <article>. The same applies to <li>: they are not blue because they are placed inside of <ul>.

General siblings combinator

The A ~ B selector with the ~ combinator applies the style to all the B elements that follow the A elements (immediately after or with other elements between them) and have the same parent element as A. It's called a general sibling combinator.

Let's remove the previous CSS styles of the page and add the following:

h3 ~ p {
    color: deeppink;
}

The result is:

HTML page with pink text

All <p> placed after <h3> inside each <article> are now pink. The sentence "I love it." is also <p> but it's not colored pink because it's not a sibling of <h3>.

Adjacent sibling combinator

The A + B selector with the + combinator applies the style to all the B elements that are placed immediately after the A elements and have the same parent as A. It's called an adjacent sibling combinator. Let's try to apply it to our previous example:

h3 + p {
    color: deeppink;
}

The result will be as follows:

HTML page with pink and black text

Now only those <p> that are placed immediately after <h3> are colored pink.

Using combinators with other selectors

Combinators can also be used with other selectors. For example, if you want to select all <img> on a page that have the alt attribute and are placed inside <p>, you can use a descendant combinator and an attribute selector:

p img[alt] {
    ...
}

Conclusion

Your toolset for CSS styling has just become more complete. Combinators of all four types will prove to be useful for different page constructions. Also, it's good to know and remember the key terms like child/parent, descendant/ancestor, and siblings, because they are used widely in many other fields. Take your time understanding the relationships and remembering the syntax, and let's move on to some questions and practice tasks!

197 learners liked this piece of theory. 4 didn't like it. What about you?
Report a typo