This question may remind you of What is the difference between :first-child and :first-of-type? — and in fact, a lot of parallels can be drawn between the two. Where this question greatly differs from the other is the arbitrary integer argument X, as in :nth-child(X)
and :nth-of-type(X)
. They're similar in principle to their "first" and "last" counterparts, but the potentially matching elements vary greatly based on what's actually in the page.
But first, some theory. Remember that simple selectors are independent conditions. They remain independent even when combined into compound selectors. That means that the p
neither is influenced by, nor influences, how :nth-child()
or :nth-of-type()
matches. Combining them this way simply means that elements must match all of their conditions simultaneously in order to match.
Here's where things get interesting. This independent matching means I can get pretty creative in how I express compound (and complex) selectors in terms of plain English, without changing the meaning of the selectors. In fact, I can do so right now in a way that makes the difference between :nth-child(2)
and :nth-of-type(2)
seem so significant that the pseudo-classes might as well be completely unrelated to each other (except for the "siblings" part anyway):
All of a sudden, they sound really different! And this is where a bit of explanation helps.
Any element may only have a single child element matching :nth-child(X)
for any integer X at a time. This is why I've chosen to emphasize "the second child" by mentioning it first. In addition, this child element will only match p:nth-child(X)
if it happens to be of type p
(remember that "type" refers to the tagname). This is very much in line with :first-child
and :last-child
(and, similarly, p:first-child
and p:last-child
).
There's two aspects to :nth-of-type(X)
on the other hand:
Because the "type" in :nth-of-type()
is the same concept as the "type" in a type selector, this family of pseudo-classes is designed to be used in conjunction with type selectors (even though they still operate independently). This is why p:nth-of-type(2)
can be expressed as succinctly as "Select the second p
element among its siblings." It just works!
However, unlike :first-of-type
and :last-of-type
, the X requires that there actually be that many child elements of the same type within their parent element. For example, if there's only one p
element within its parent, p:nth-of-type(2)
will match nothing within that parent, even though that p
element is guaranteed to match p:first-of-type
and p:last-of-type
(as well as, by extension, p:only-of-type
).
An illustration:
<div class="parent">
<p>Paragraph</p>
<p>Paragraph</p> <!-- [1] p:nth-child(2), p:nth-of-type(2) -->
<p>Paragraph</p>
<footer>Footer</footer>
</div>
<div class="parent">
<header>Header</header>
<p>Paragraph</p> <!-- [2] p:nth-child(2) -->
<p>Paragraph</p> <!-- [3] p:nth-of-type(2) -->
<footer>Footer</footer>
</div>
<div class="parent">
<header>Header</header>
<figure>Figure 1</figure>
<p>Paragraph</p> <!-- [4] -->
<footer>Footer</footer>
</div>
<div class="parent">
<header>Header</header>
<p>Paragraph</p> <!-- [2] p:nth-child(2) -->
<figure>Figure 1</figure>
<hr>
<figure>Figure 2</figure> <!-- [5] .parent > :nth-of-type(2) -->
<p>Paragraph</p> <!-- [5] .parent > :nth-of-type(2) -->
<p>Paragraph</p>
<footer>Footer</footer>
</div>
What's selected, what's not, and why?
Selected by both p:nth-child(2)
and p:nth-of-type(2)
The first two children of this element are both p
elements, allowing this element to match both pseudo-classes simultaneously for the same integer argument X, because all of these independent conditions are true:
- it is the second child of its parent;
- it is a
p
element; and
- it is the second
p
element within its parent.
Selected by p:nth-child(2)
only
This second child is a p
element, so it does match p:nth-child(2)
.
But it's the first p
element (the first child is a header
), so it does not match p:nth-of-type(2)
.
Selected by p:nth-of-type(2)
only
This p
element is the second p
element after the one above, but it's the third child, allowing it to match p:nth-of-type(2)
but not p:nth-child(2)
. Remember, again, that a parent element can only have one child element matching :nth-child(X)
for a specific X at a time — the previous p
is already taking up the :nth-child(2)
slot in the context of this particular parent element.
Not selected
This p
element is the only one in its parent, and it's not its second child. Therefore it matches neither :nth-child(2)
nor :nth-of-type(2)
(not even when not qualified by a type selector; see below).
Selected by .parent > :nth-of-type(2)
This element is the second of its type within its parent. Like :first-of-type
and :last-of-type
, leaving out the type selector allows the pseudo-class to potentially match more than one element within the same parent. Unlike them, how many it actually matches depends on how many of each element type there actually are.
Here, there are two figure
elements and three p
elements, allowing :nth-of-type(2)
to match a figure
and a p
. But there's only one header
, one hr
, and one footer
, so it won't match elements of any of those types.
In conclusion, :nth-child()
and :nth-of-type()
, with an integer argument X (i.e. not in the form An+B with a coefficient A of n), function pretty similarly to :first-child
/:last-child
and :first-of-type
/:last-of-type
, with the major difference being that the argument, along with the page itself, influences how many different elements may be matched with :nth-of-type()
.
Of course, there's a whole lot more to :nth-child()
and :nth-of-type()
than just a simple integer argument, but needless to say the details and possibilities thereof are outside the scope of this question.
child, but the second child is not a
but some other element, p:nth-child(2) will match nothing, and p:nth-of-type(2) will match the second
no matter where it is relative to its siblings. If the second child is a
, but it is the *first*
(e.g. the first child is actually an
), p:nth-child(2) will match that
, and p:nth-of-type(2) will either match nothing or some other
depending on whether there are any other
elements in the parent.
– BoltClock Sep 25 '18 at 08:39