The following is an extract from our book, CSS Master, written by Tiffany B. Brown. Copies are sold in stores worldwide, or you can buy it in ebook form here.
CSS also provides selectors for matching elements based on their position in the document subtree. These are known as child–indexed pseudo-classes, because they rely on the position or order of the element rather than its type, attributes, or ID. There are five:
-
:first-child
-
:last-child
-
:only-child
-
:nth-child()
-
:nth-last-child()
:first-child
and :last-child
As you’ve probably guessed from the names, the :first-child
and :last-child
pseudo-classes make it possible to select elements that are the first child or last child of a node (element). As with other pseudo-classes, :first-child
and :last-child
have the fewest side effects when qualified by a simple selector.
Let’s take a look at the HTML and CSS below:
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<title>:first-child and :last-child</title>
<style type="text/css">
body {
font: 16px / 1.5 sans-serif;
}
:first-child {
color: #e91e63;
}
:last-child {
color: #4caf50;
}
</style>
</head>
<body>
<h2>List of fruits</h2>
<ul>
<li>Apples</li>
<li>Bananas</li>
<li>Blueberries</li>
<li>Oranges</li>
<li>Strawberries</li>
</ul>
</body>
</html>
You can see what this looks like in the figure below.
Because :first-child
is unqualified, both the h2
element and first li
element are hot pink. After all, h2
is the first child of body
, and li
is the first child of the ul
element. But why are the remaining li
elements green? Well, that’s because :last-child
is also unqualified, and ul
is the last child of body
. We’ve essentially typed *:first-child
and *:last-child
.
If we qualify :first-child
and :last-child
by adding a simple selector, it all makes more sense. Let’s limit our selection to list items. Change :first-child
to li:first-child
and :last-child
to li:last-child
. the image below shows the result.
:nth-child()
and :nth-last-child()
The ability to select the first and last children of a document is fine. But what if we want to select odd or even elements instead? Perhaps we’d like to pick the sixth element in a document subtree, or apply styles to every third element. This is where the :nth-child()
and the :nth-last-child()
pseudo-classes come into play.
Like :not()
, :nth-child()
and :nth-last-child()
are also functional pseudo-classes. They accept a single argument, which should be either:
-
the
odd
keyword -
the
even
keyword -
an integer such as 2 or 8, or
-
an argument in the form An+B[5] where A is a step interval, B is the offset, and n is a variable representing a positive integer.
That last item has a degree of complexity. We’ll come back to it in a moment.
What’s the difference between :nth-child()
and :nth-last-child()
? The starting point: :nth-child()
counts forwards and :nth-last-child()
counts backwards. CSS indexes use counting numbers and start with one rather than zero.
Both :nth-child()
and :nth-last-child()
are useful for alternating patterns. Creating zebra-striped table row colors is the perfect use case. The CSS that follows gives even-numbered table rows a light bluish-gray background, the result of which can be seen in the figure below:
tr:nth-child(even) {
background: rgba(96, 125, 139, 0.1);
}
Switching :nth-child
to :nth-last-child
inverts this banding, since the counting begins from the bottom, shown below.
How about trying some complex examples using more complex arguments? We’ll start with the document shown below, which contains 20 items.
With :nth-child()
and :nth-last-child()
, we can select a single child at a particular position. We can select all of the children after a particular position, or we can select elements by multiples, with an offset. Let’s change the background color of the sixth item:
.item:nth-child(6) {
background: #e91e63;
}
This gives us the result below.
But what if we want to select every third element? Here’s where the An+B syntax comes in:
.item:nth-child(3n) {
background: #e91e63;
}
Again, A is a step interval. It’s almost like a multiplier for n, which starts at 1. So if A = 3, then 3n would match the 3rd, 6th, 9th, and so on elements. That’s exactly what happens, as you can see in below.
Here’s where matters become a little more interesting. We can use :nth-child()
and :nth-last-child()
to select all elements after a certain point. Let’s try selecting all but the first seven elements:
.item:nth-child(n+8) {
background: #e91e63;
}
Here, there is no step value. As a result, n+8
matches every element n beginning with the eighth element, as shown below.
Note: Negative Offsets
Negative offset and range values are also valid. Using :nth-child(-n+8)
would invert our selection, and match the first eight elements.
We can also use the offset and step values to select every third element, starting with the fifth:
.item:nth-child(3n+5) {
background: #e91e63;
}
You can see the results of this selector below.
:only-child
The :only-child
pseudo-class matches elements if they are the only child of another element. Below are two unordered lists. The first has one item while the second contains three:
<ul>
<li>Apple</li>
</ul>
<ul>
<li>Orange</li>
<li>Banana</li>
<li>Raspberry</li>
</ul>
Using li:only-child{color: #9c27b0;}
will select <li>Apple</li>
, since it’s the only child of our first list. None of the items in the second list match, however, because there are three siblings. You can see what this looks like below.
:empty
It’s also possible to select elements that have no children using the :empty
pseudo-class. Now when we say :empty
, we mean empty. In order for an element to match the :empty
pseudo-class, it can’t contain anything else—not even whitespace. In other words, <p></p>
will match, but <p> </p>
will not.
Sometimes WYSIWYG (What You See Is What You Get) editors insert empty p
elements to your content. You could use :empty
in combination with the :not()
pseudo-class to avoid applying styles to these elements; for example p:not(:empty)
.
Selecting Elements of a Particular Type by their Index
The pseudo-classes discussed in the previous section match elements if they occupy the given position in a document subtree. For instance, p:nth-last-child(2)
selects every p
element that is the next-to-last element of its parent.
In this section, we’ll discuss typed child-indexed pseudo-classes. These pseudo-classes also match elements based on the value of their indexes; however, matches are limited to elements of a particular type. Selecting the fifth p
element, or even-indexed h2
elements, for example.
There are five such pseudo-classes with names that mirror those of their untyped counterparts:
-
:first-of-type
-
:last-of-type
-
:only-of-type
-
:nth-of-type()
-
:nth-last-of-type()
The difference between these and child-indexed pseudo-classes is a subtle one. Where p:nth-child(5)
matches the fifth item only if it is a p
element, p:nth-of-type(5)
matches all p
elements, then finds the fifth p
element among those.
Let’s start with a slightly different document. It still has 20 items, but some of them are p
elements and some of them are div
elements. The p
elements have rounded corners, as can be seen below.
Using :first-of-type
, :last-of-type
, and :only-type
With :first-of-type
, we can select the first element that matches a selector. How about we give our first p
element a lime green background:
p:first-of-type {
background: #cddc39;
}
This will match every p
element that’s the first p
element of its parent, shown below.
The :last-of-type
pseudo-class works similarly, matching the last such element of its parent as presented below. However, :only-of-type
will match an element if it’s the only child element of that type of its parent, illustrated underneath.
Let’s look at another example of using :first-of-type
, but this time with a pseudo-element. Remember the ::first-letter
pseudo-element from earlier in this chapter? Well, as you saw, it created an initial capital for every element to which it was applied. How about we go one step further, and limit this initial capital to the first paragraph instead:
p:first-of-type::first-letter {
font: bold italic 3em / .5 serif;
color: #3f51b5;
}
As the image below shows, now our paragraph will have an initial capital, even if it’s preceded by a headline.
Using :nth-of-type
and :nth-last-of-type
The :nth-of-type()
and :nth-last-of-type()
are also functional pseudo-classes. They accept the same arguments as :nth-child()
and :nth-last-child()
. But like :first-of-type
and :last-of-type
, the indexes resolve to elements of the same type. For example, to select the first p
element and every other subsequent p
element, we can use the odd
keyword with :nth-of-type()
:
p:nth-of-type(odd) {
background: #cddc39;
color: #121212;
}
As you can see from teh image below, this only matches odd-numbered p
elements, rather than odd-numbered children.
Similarly, using :nth-last-of-type(even)
selects even-numbered p
elements, but the count begins from the last p
element in the document—in this case, item 18 (shown below).
If this still seems fuzzy, play with Paul Maloney’s Nth-Test tool, or view the examples at Nth Master. Both projects are excellent ways to learn more about these pseudo-classes.
[5] This An+B syntax is described in CSS Syntax Module Level 3.