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:
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:
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:
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:
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:
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!