CSS Pseudo-classes: Styling Elements Based on Their Index

Tiffany Brown
Share

cssmasterthumb

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.

SelectorFirstChild

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.

SelectorFirstChild2

: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);
}

SelectorNthChild1

Switching :nth-child to :nth-last-child inverts this banding, since the counting begins from the bottom, shown below.

SelectorNthChild2

How about trying some complex examples using more complex arguments? We’ll start with the document shown below, which contains 20 items.

SelectorNthChild3

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.

SelectorNthChild4

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.

SelectorNthChild5

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.

SelectorNthChild6

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.

SelectorNthChild7

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

SelectorOnlyChild

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

NthOfType1

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.

NthOfType2

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.

NthOfType3

NthOfType4

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.

NthOfType5

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.

NthOfTypeOdd

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).

NthLastTypeEven

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.