108

The issue lies within this CSS and HTML. Here is a link to jsFiddle with the sample code.

HTML

<ul>
    <li class"complete">1</li>
    <li class"complete">2</li>
    <li>3</li>
    <li>4</li>
</ul>

CSS

li.complete:last-child {
    background-color:yellow;
}

li.complete:last-of-type {
    background-color:yellow;
}

Shouldn't either of these lines of CSS target the last li element with the "complete" class?

This query in jQuery doesn't target it either:

$("li.complete:last-child");

But this one does:

$("li.complete").last();

li {
  background-color: green;
}
li.complete:first-child {
  background-color: white;
}
li.complete:first-of-type {
  background-color: red;
}
li.complete:last-of-type {
  background-color: blue;
}
li.complete:last-child {
  background-color: yellow;
}
<ul>
  <li class="complete">1</li>
  <li class="complete">2</li>
  <li>3</li>
  <li>4</li>
</ul>
Harry
  • 87,580
  • 25
  • 202
  • 214
Nej Kutcharian
  • 4,054
  • 5
  • 21
  • 27
  • @PatrickEvans What do you mean? http://api.jquery.com/category/selectors/ or more specifically http://api.jquery.com/last-child-selector/ – Nej Kutcharian Sep 25 '13 at 02:51
  • Does this answer your question? [How can I select the last element with a specific class, not last child inside of parent?](https://stackoverflow.com/questions/7298057/how-can-i-select-the-last-element-with-a-specific-class-not-last-child-inside-o) – fabpico Apr 12 '22 at 08:49

3 Answers3

183

:last-child will not work if the element is not the VERY LAST element

I think it's crucial to add/emphasize that :last-child will not work if the element is not the VERY LAST element in a container. For whatever reason it took me hours to realize that, and even though Harry's answer is very thorough I couldn't extract that information from "The last-child selector is used to select the last child element of a parent."

Suppose this is my selector: a:last-child {}

This works:

<div>
    <a></a>
    <a>This will be selected</a>
</div>

This doesn't:

<div>
    <a></a>
    <a>This will no longer be selected</a>
    <div>This is now the last child :'( </div>
</div>

It doesn't because the a element is not the last element inside its parent.

It may be obvious, but it was not for me...


Sidebar: This may seem like a ridiculous gotcha, but the devil's always in the details. :last-of-type may fit your needs in most cases (and feels intuitive) but :last-child definitely serves a purpose. It offers greater specificity (targeting only those elements which are, in-fact, the very last child in a parent). It depends on your use-case.

Govind Rai
  • 14,406
  • 9
  • 72
  • 83
  • 19
    Double man! Also, nth-last-child works the same. Use last-of-type instead. – Andri Apr 08 '17 at 20:46
  • 1
    Thank you so much for this info, it helped me figure out why my CSS wasn't working. I'm sure the sticklers would argue this is correct, but I sure wish the pseuoclass was last-child of the selector you're modifying, and didn't only work on last child of the whole node list. – BeniRose Apr 28 '17 at 00:52
  • This helped me. I had three labels and a @Html.ValidationMessageFor for the input and the last child was the ValidationMessage... – Spyros_Spy Dec 21 '19 at 17:03
  • This is super useful. I spent hours trying to get my li:last-child working, but it was followed by div. last-of-type worked for me – McAwesome Jun 01 '21 at 09:37
  • 1
    After struggling with this issue for a while, I searched stackoverflow where I saw this answer, which already had an orange upvote. Seems like this is not the first time you've helped me out haha – Robin Sep 27 '21 at 16:30
  • @Robin I have that same experience on StackOverflow **_all_** the time. :D – Govind Rai Sep 16 '22 at 17:49
158

The last-child selector is used to select the last child element of a parent. It cannot be used to select the last child element with a specific class under a given parent element.

The other part of the compound selector (which is attached before the :last-child) specifies extra conditions which the last child element must satisfy in-order for it to be selected. In the below snippet, you would see how the selected elements differ depending on the rest of the compound selector.

.parent :last-child{ /* this will select all elements which are last child of .parent */
  font-weight: bold;
}

.parent div:last-child{ /* this will select the last child of .parent only if it is a div*/
  background: crimson;
}

.parent div.child-2:last-child{ /* this will select the last child of .parent only if it is a div and has the class child-2*/
  color: beige;
}
<div class='parent'>
  <div class='child'>Child</div>
  <div class='child'>Child</div>
  <div class='child'>Child</div>
  <div>Child w/o class</div>
</div>
<div class='parent'>
  <div class='child'>Child</div>
  <div class='child'>Child</div>
  <div class='child'>Child</div>
  <div class='child-2'>Child w/o class</div>
</div>
<div class='parent'>
  <div class='child'>Child</div>
  <div class='child'>Child</div>
  <div class='child'>Child</div>
  <p>Child w/o class</p>
</div>

To answer your question, the below would style the last child li element with background color as red.

li:last-child{
    background-color: red;
}

But the following selector would not work for your markup because the last-child does not have the class='complete' even though it is an li.

li.complete:last-child{
    background-color: green;
}

It would have worked if (and only if) the last li in your markup also had class='complete'.


To address your query in the comments:

@Harry I find it rather odd that: .complete:last-of-type does not work, yet .complete:first-of-type does work, regardless of it's position it's parents element. Thanks for your help.

The selector .complete:first-of-type works in the fiddle because it (that is, the element with class='complete') is still the first element of type li within the parent. Try to add <li>0</li> as the first element under the ul and you will find that first-of-type also flops. This is because the first-of-type and last-of-type selectors select the first/last element of each type under the parent.

Refer to the answer posted by BoltClock, in this thread for more details about how the selector works. That is as comprehensive as it gets :)

Community
  • 1
  • 1
Harry
  • 87,580
  • 25
  • 202
  • 214
  • 4
    This is so silly but I think you're right. Hopefully they add this to the CSS4 spec. – Nej Kutcharian Sep 25 '13 at 02:58
  • @NejKutcharian: Yes, I think it has been in proposal for a long time. – Harry Sep 25 '13 at 02:59
  • @Harry I find it rather odd that: .complete:last-of-type does not work, yet .complete:first-of-type does work, regardless of it's position it's parents element. Thanks for your help. – Nej Kutcharian Sep 25 '13 at 03:03
  • @NejKutcharian: Ok got it. `.complete:first-of-type` works in the fiddle because it is still the first element of type `li` within the parent. Try to add `
  • 0
  • ` as the first element under `li` and you will find that `first-of-type` also flops. – Harry Sep 25 '13 at 03:06