Suppose we have the following HTML:
<div class="site-main"> <article>First article</article> <article>Second article</article> <article>Third article</article> <article>Fourth article</article> <nav class="site-nav">Previous ... Next</nav> </div><!-- .site-main -->
We want to give the last article
element a black border. So we write:
.site-main article:last-child { border: 5px solid black; }
But this does not work!
While our intention is to select the last article element, the :last-child
pseudo-selector will not do so in this case. Why? There is an additional nav
element below all the article
, and this element truly is the last-child
of div.site-main
. In the case above, the expression article:last-child
causes the browser’s rendering engine to grab the last child, which is a nav
. Since this is not the correct element type, the browser does not apply the desired styles to it.
Use last-of-type
If there was no nav
element, then the last article
would have been styled as intended. However, since we care to style the last child that is an article
element, it is correct to use the :last-of-type
selector. Now our CSS works:
.site-main article:last-of-type { border: 5px solid black; }
Other X-of-type pseudo-selectors
In addition to last-of-type
, there are also:
first-of-type
– selects the first element of its parent with the matching typenth-of-type()
– selects the nth child of its parent with the matching type
These pseudo-selectors are extremely useful if you are trying to apply styles among a bunch of distinct elements.
But wait! There’s another caveat
Consider the following:
<div class="site-main"> <section class="banner">First section</section> <section class="content">Second section</section> <section class="content">Third section</section> </div>
section:first-of-type { /* First section */ } section.banner:first-of-type { /* First section */ } section.content:first-of-type { /* No effect, why?? */ }
To understand why the last selector fails, we must understand what type means. The type in X-of-type refers only to the type of element (e.g. a <p>
or <div>
), and not its attributes such as ID or classes. In the example above: The reason why section.content:first-of-type
didn’t do anything is because the first section
element did not have class content
. The second line worked, but is overqualified, i.e. the .banner
is completely redundant.
In summary, when using X-of-type
selectors, don’t attempt to introduce class, ID, or other attribute selectors into the mix. Doing so will only deceive you.
Putting it all together
The example below demonstrates the use of X-of-type selectors on a snippet of HTML code. A live demo is also available at JSFiddle.
&lt;div class=&quot;site-main&quot;&gt; &lt;article id=&quot;art-1&quot;&gt;&lt;/article&gt;&lt;!-- #1 --&gt; &lt;aside class=&quot;tweet&quot;&gt;&lt;/aside&gt;&lt;!-- #2 --&gt; &lt;article id=&quot;art-2&quot;&gt;&lt;/article&gt; &lt;aside class=&quot;quote&quot;&gt;&lt;/aside&gt;&lt;!-- #5 --&gt; &lt;section class=&quot;ads&quot;&gt;&lt;/section&gt; &lt;aside class=&quot;quote&quot;&gt;&lt;/aside&gt;&lt;!-- #4, 5 --&gt; &lt;article id=&quot;art-3&quot;&gt;&lt;/article&gt; &lt;aside class=&quot;tweet&quot;&gt;&lt;/aside&gt; &lt;aside class=&quot;quote&quot;&gt;&lt;/aside&gt;&lt;!-- #5 --&gt; &lt;article&gt;3&lt;/article&gt;&lt;!-- #3 --&gt; &lt;nav class=&quot;site-navigation&quot;&gt;1 2 3 4 Next...&lt;/nav&gt; &lt;/div&gt;
aside:first-child { /* affects nothing */ } article:last-child { /* affects nothing */ } article:first-of-type { /* 1 */ } aside:first-of-type { /* 2 */ } aside.quote:first-of-type { /* affects nothing */ } article:last-of-type { /* 3 */ } aside:nth-child(3) { /* affects nothing */ } aside:nth-of-type(3) { /* 4 */ } aside.quote:nth-of-type(odd) { /* 5 */ }
Browser compatibility
All of these pseudo-selectors were introduced with CSS3. To see the browser support for these selectors, check out this page on Can I Use.
Very nice tutorial, Thanks For This Great Article.