Nicky Meuleman
Posted on July 29, 2019
Targeting a specific HTML-element doesn't have to cause headaches.
Many are comfortable with selecting by class (with a dot .
) or IDs (with the octothorpe #
). Those are far from the only tools in the CSS shed.
I hope after reading this blogpost your toolchest will be a little bit bigger!
I realize most selections in this post could be written simpler.
The point is to showcase the different kinds of CSS-selectors and how they work!
CSS family tree
An HTML-element is a descendant of another element when it is found within the other element.
An HTML-element is a child of another element when it is found a single level within the other element.
Ok, that explanation sounded confusing even to me, and I wrote it! let's look at an example.
<div class="quarter-finals">
<div class="semi-finals">
<div class="final">
<div class="champion"></div>
</div>
</div>
</div>
In this example, quarter-finals
is the ancestor of every other element.
final
is the child(so also descendant!) of semi-finals
.
<div class="quarter-finals">
<div class="semi-finals">
<div class="final">
<div class="champion"></div>
<div class="runner-up"></div>
</div>
</div>
</div>
Apart from being an ancestor/parent or descandant/child, an element can also be a sibling of another element.
This is the case when the element is found at the same level of the other element.
The runner-up
is the sibling of the champion
.
Combinator selectors
These characteristics can be leveraged to select a certain element in CSS.
Say you want to give all descendants of the quarter-finals a background color. You could do something like
.quarter-finals div {
background-color: black;
}
That space between .quarter-finals
and div
is the descendant combinator. (the other space before the curly brace is just there for formatting)
You could read that selector like this: "Select any div-element that is a descendant of any element with the class quarter-finals".
Let's overwite the black background on semi-finals
with a red one.
We'll use the child combinator for this.
.quarter-finals > div {
background-color: red;
}
The caret between .quarter-finals
and div
is the child combinator.
You could read that selector like this: "Select any div-element that is a child of any element with the class quarter-finals".
The runner-up
is looking a bit sad, it got so close to the championship title.
We can select it with the adjacent sibling combinator.
.champion + div {
background-color: yellow;
}
The plus between .champion
and div
is the adjacent sibling combinator.
You could read that selector like this: "Select any div-element that immediately follows an element with the class champion and has the same parent."
If any element comes between the .champion
and the .runner-up
so they no longer directly follow eachother, the adjacent sibling combinator we wrote won't work anymore. That's a good time to use the general sibling combinator.
<div class="quarter-finals">
<div class="semi-finals">
<div class="final">
<div class="champion"></div>
<span>won against</span>
<div class="runner-up"></div>
</div>
</div>
</div>
This means our .runner-up
will no longer have a yellow background. It doesn't immediately follow the .champion
anymore so the CSS we wrote above won't style it. Let's fix it
.champion ~ div {
background-color: yellow;
}
The tilde between .champion
and div
is the general sibling combinator.
You could read that selector like this: "Select any div-element that follows an element with the class champion and has the same parent."
Pseudo-class selectors
If you want to apply CSS-rules based on the state of an element, or structure (eg: only target every odd element) within that element, pseudo-classes are for you! You can recognize them by their colon :
.
a:hover {
color: white;
}
The rule above only targets a-elements that are being hovered. It falls under the state part.
If we want to select the first element in a list, we rely on a structure pseudoselector, like :first-child
We forgot to give the .champion
above a special color.
.final :first-child {
background-color: gold;
}
Notice that space between .final
and :first-child
? It's the descendant selector we talked about earlier. It should be there because pseudo-classes only target the element it is attached to.
This selector is quite specific, what if a different element appears above the .champion
?
<div class="quarter-finals">
<div class="semi-finals">
<div class="final">
<span>in this match</span>
<div class="champion"></div>
<span>won against</span>
<div class="runner-up"></div>
</div>
</div>
</div>
Gone is that glorious background-color for the champion. :first-child
does exactly what it says, it selects the element it is attached to if that element is the first child. It's not attached to anything (but whitespace), you say? Think of it like it's written like this: .final *:first-child
Don't fear for the champion, we can select the first-child of a certain type with :first-of-type
.final div:first-of-type {
background-color: gold;
}
It doesn't stop at the first of something. You also have to opposite: :last-child
and :last-of-type
.
You can also select an element at a specific position with :nth-child()
and :nth-of-type()
.
What should be between those parenteses is a number. Or simple math that evaluates to numbers.
li:nth-of-type(3)
will select the third list item. Not the fourth one like a lot of programmers who are accustomed to start counting at 0 would think!
li:nth-child(2n + 1) {
font-weight: bold;
}
Will bold every odd element in a list. The browser runs that simple equation and enters 0,1,2,... in place of n
and uses the resulting number.
Those are only a couple examples, I encourage you to explore more about pseudo-classes on MDN.
Pseudo-elements
Closely related to the pseudo-classes are the pseudo-elements.
Where the pseudo-classes selected an element.
The pseudo-elements will select part of an element.
The syntax is also very similar.
With pseudo-classes you use a single colon :
.
With pseudo-elements you use two colons ::
Many browsers will let you get away with only using a single colon. Just because you can doesn't mean you should.
span::first-letter {
color: green;
}
This bit of CSS would make the starting letters of our 2 spans
in our example green.
There are many more options, you can read/learn about them on MDN.
Attribute selectors
You can also select elements based on their attributes and the values of their attributes. Which is really what you are already doing when using the ID(#
) or class(.
) selector if you think about it.
<div class="really-fast-drive lemans-winner tiny">
Fernando Alonso
</div>
<div class="really-slow-driver non-lemans-winner tall">
Nicky Meuleman
</div>
The simplest form is just checking if an attribute is present.
div[class] {
/* Fernando Alonso is so tiny in real life */
}
This will only target divs that have a class attribute.
To match an element that has an exact value for an attribute
span[class="really-fast-driver"] {
font-weight: bold;
}
This would make Alonso bold right? Wrong! The value has to be exact, we are only specifying one class, not all of them (they also needed to be in exactly the right order for this to work.)
div[class~="really-fast-driver"] {
font-weight: bold;
}
This hits the spot.
The tilde selects any element with an attribute whose value is within a space-seperated list
Let's select all elements that have a class attribute that starts with really
div[class^="really"] {
color: red;
}
The up-caret selects any element with an attribute that starts with the given value.
Remember: this wouldn't work if really-fast-driver
and really-slow-driver
weren't the first in the list of classes!
div[class|="really"] {
color: red;
}
The pipe selects any element with an attribute that starts with the given value, optionally followed by a dash.
div[class$="driver"] {
background-color: black;
}
The dollar-sign selects any element with an attribute that ends with the given value.
div[class*="lemans"] {
border: 1px solid blue;
}
The asterisk selects any element with an attribute that contains the substring of the given value.
Case sensitivity
Including an i
before the closing bracket will make the selector insensitive to case.
div[class*="LeMans" i] {
border: 1px solid blue;
}
The selector above will match the same elements as the example without the capitals and the i.
Combining
We already had a taste of combining when we used the descendant selector in the section about pseudo-classes.
How would you go about selecting the label for a checkbox that is checked? You have this HTML to work with, the author didn't follow proper accessibility-guidelines and left off the for
attribute.
<input type="checkbox">
<label>Belgium wins the world cup</label>
A possible way to do that would be:
input[type="checkbox"]:checked + label {
color: red;
}
You could read that selector like this: "Select any label
element that has an input
immediately preceding it. That input should have checkbox
as value for the type
attribute and it should be checked."
The CSS-rule will only be applied when all these conditions are met!
Even more tools
CSS has many more useful features, you can come accross some symbols you don't recognize as being at home in CSS.
You might even be looking at a CSS (pre)processor, like SASS/SCSS. A symbol you see very often there that's not available in regular CSS is the ampersand &
. It is used to refer to the the parent selector (yes, nesting in CSS 🤯).
CSS-tricks has a great article that will help you understand it.
Sources used:
Posted on July 29, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.