Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CSS :last-of-type not working in combination with :not

I've got something that behaves the same way as the provided snippet. I've got a list of items, that when one is selected, I want that one to be displayed at the top of the list, which I can easily do with flex ordering. Only one can be selected at a time. But since each of the list elements have borders, the last element shouldn't have a border. Simply using the li:last-of-type works fine for most cases, except for when the it's the last list item that is selected.

I've tried several selectors that should select the last list item in a unordered list that doesn't have a given class, but the last-of-type selector doesn't seem to be behaving properly.

In case it isn't clear in the code, the selector I'm referring to is .selection li:not(.selected):last-of-type. And the problem is the double border at the bottom of the list. It should be a single border coming from the unordered list element.

.selection ul {
  display: flex;
  flex-direction: column;
  border: .15rem solid #666;
}

.selection li {
  order: 2;
  border-bottom: .15rem dashed #666;
}

.selection li.selected {
  order: 1;
}

/** This selector should work in my mind, but it doesn't **/
.selection li:not(.selected):last-of-type {
  border-bottom: none;
}

/** Non-important styles to put it into context **/
ul { list-style: none; padding: 0; width: 25%; margin: 2rem auto; }
li { padding: 1rem 2rem; background-color: #ebebeb; }
li:hover { background-color: #ccc; }
a { color: inherit; text-decoration: none; }
<div class="selection">
  <ul>
    <li><a href="">1</a></li>
    <li><a href="">2</a></li>
    <li class="selected">3</li>
  </ul>
</div>

I've also created this Codepen snippet to demonstrate the problem.

I have a feeling this is a problem with the :last-of-type selector, and maybe I'm using it wrong, but in my mind, the above selector should work.

Any help or insight into how to fix this, or another way to select the element, or insight into why this doesn't work would be appreciated.

like image 513
Pavlin Avatar asked Feb 17 '16 15:02

Pavlin


People also ask

How do I get the last element of a type in CSS?

The :last-of-type selector allows you to target the last occurence of an element within its container. It is defined in the CSS Selectors Level 3 spec as a “structural pseudo-class”, meaning it is used to style content based on its relationship with parent and sibling content. ).

How do you not get the last element in CSS?

There comes the use of pseudo selectors. This article explains the :not(:last-child):after selector. This selector does not select the element after the last child element. It is mostly used to add the content after every child element except the last.

Does last-of-type work on classes?

class is not possible with last-of-type . Edit: To actually add something constructive to this answer, you could fallback to JavaScript to locate this selector, or revise your markup/CSS.

How do you take the nth last of a type in CSS?

The :nth-last-of-type(n) selector matches every element that is the nth child, of a particular type, of its parent, counting from the last child. n can be a number, a keyword, or a formula.


2 Answers

Consider this alternative approach:

  • Get rid of the :not selector entirely
  • Give the ul a border only on the left, right and top
  • Apply the bottom border only to the li

ul {
    display: flex;
    flex-direction: column;
    border-top: .15rem solid #666;
    border-left: .15rem solid #666;
    border-right: .15rem solid #666;
}

li { border-bottom: .15rem solid #666; }

Revised Codepen


Here's why your selector isn't working:

.selection li:not(.selected):last-of-type { border-bottom: none; }

It appears to me that you're saying you want to match the last li element as long as it doesn't have the class selected.

But this isn't how last-of-type works. If the li is a sibling, the selector will match it, regardless of class. The selector matches DOM elements. This means that even if you applied display: none to an li, the selector would still match it.

6.6.5.9. :last-of-type pseudo-class

The :last-of-type pseudo-class represents an element that is the last sibling of its type in the list of children of its parent element.

source: https://drafts.csswg.org/selectors-3/#last-of-type-pseudo

More details: How to get nth-child selector to skip hidden divs

like image 75
Michael Benjamin Avatar answered Oct 23 '22 13:10

Michael Benjamin


Instead of trying to remove border-bottom from the last unselected item (which is hard or impossible in CSS), it's way easier to set a border-top to each item and remove the extra one from the item visually above others, which is... .selected!
Your constraint of having:

a different border on my list elements than on the general outside border

is permitted with this solution.

Updated pen

.selection ul {
  display: flex;
  flex-direction: column;
  border: .15rem solid #666;
}

.selection li {
  order: 2;
  border-top: .15rem dashed #666;
  background: lightblue;
}

.selection li.selected {
  order: 1;
  border-top: none;
  background: tomato;
}

/** Non-important styles to put it into context **/
ul { list-style: none; padding: 0; width: 25%; margin: 2rem auto; }
li { padding: 1rem 2rem; background-color: #ebebeb; }
li:hover { background-color: #ccc; }
a { color: inherit; text-decoration: none; }
<div class="selection">
  <ul>
    <li><a href="">1</a></li>
    <li><a href="">2</a></li>
    <li class="selected">3</li>
  </ul>
</div>
like image 21
FelipeAls Avatar answered Oct 23 '22 12:10

FelipeAls