technophobia
technophobia

Reputation: 2629

Selecting last consecutive sibling

How do I select the last element in a sequence of adjacent elements?

Consider the following markup:

HTML

<ul>
    <li class="foo">...</li>
    <li class="foo">...</li>
    <li class="foo">...</li> <!-- bingo -->

    <li class="bar">...</li>
    <li class="bar">...</li>
    <li class="bar">...</li>
    <li class="bar">...</li> <!-- bingo -->

    <li class="foo">...</li> <!-- bingo -->

    <li class="bar">...</li>
    <li class="bar">...</li> <!-- bingo -->
</ul>

The number of consecutive foo or bar elements is dynamic. Also, assume the markup cannot be modified in any way.

Selecting adjacent elements is pretty straight forward:

CSS

.foo + .foo, 
.bar + .bar { /* do something */ }

But selecting the last element in a series of consecutive elements, is that possible?

Upvotes: 16

Views: 2757

Answers (4)

Vahid
Vahid

Reputation: 7551

The CSS support for :has is still poor but it can solve the problem:

/* selects any .foo that has no .foo immediately after it  */
.foo:has(+ :not(.foo)) {}

Upvotes: 4

Raphael Morgan
Raphael Morgan

Reputation: 210

I don't believe there's a way to do this without changing the HTML or the way you generate it. However, if you generate it via the .append() method, all you'd have to do to use this (theoretically) is change it to .prepend(). If you do that, you can change the ul's css to have it select backwards, and then you can use the + selector like so:

(edit: I don't know how to reorder the snippet, but the JS is the least important and only there for if you want to see the .prepend() in action)

//some generation examples just for fun

foo.addEventListener('click',addKnown);
bar.addEventListener('click',addKnown);
r.addEventListener('click',addRandom);

function addKnown(e) {
  let li = document.createElement('li');
  li.className = e.target.id;
  li.textContent = e.target.id;
  document.querySelector('ul').prepend(li);
}
function addRandom(e) {
  let li = document.createElement('li');
  let type = ['foo','bar'][Math.round(Math.random())];
  li.className = type;
  li.textContent = type;
  document.querySelector('ul').prepend(li);
 }
ul {
  display: flex;
  flex-direction: column-reverse;
}

li:first-of-type,
.foo+.bar,
.bar+.foo {
  color: red
}
<ul>
  <li class="bar">bar</li> <!-- bingo -->
  <li class="bar">bar</li>

  <li class="foo">foo</li> <!-- bingo -->

  <li class="bar">bar</li> <!-- bingo -->
  <li class="bar">bar</li>
  <li class="bar">bar</li>
  <li class="bar">bar</li>

  <li class="foo">foo</li> <!-- bingo -->
  <li class="foo">foo</li>
  <li class="foo">foo</li>
</ul>

<!-- some generation examples just for fun-->
<button id="foo">Add foo</button>
<button id="bar">Add bar</button>
<button id="r">Add random</button>

I would also recommend giving the ul some sort of class or id unless you only have one or want this to be the case for all of your uls on the page.

I'm sorry, I know this isn't exactly what you wanted (because it does technically change the markup by making it backwards), but I hope it's something that's at least possible or helpful in finding something that is.

Upvotes: 0

Leo
Leo

Reputation: 293

There appears to be an nth-last-child selector:

http://css-tricks.com/almanac/selectors/n/nth-last-child/

You should be able to use it with a zero argument to get the last one

Edit: nth-last-child will indeed select the last child, which does not match the bingo's

Hopefully you can change the structure:

<ul>
   <ul>
       <li class="foo">foo 1</li>
       <li class="foo">foo 2</li>
       <li class="foo">foo 3 bingo</li>
   </ul>
   <ul>
       <li class="bar">bar 1</li>
       <li class="bar">bar 2</li>
       <li class="bar">bar 3</li>
       <li class="bar">bar 4 bingo</li>
    </ul>
    <ul>
       <li class="foo">foo 1</li>
       <li class="foo">foo 2 bingo</li>
    </ul>
</ul>

and this css will work:

.foo:nth-last-child(1),
.bar:nth-last-child(1)
{
    background-color: blue;
}

Upvotes: -2

user2726225
user2726225

Reputation: 99

You must be dynamically setting up the list.... in that case give a name or an id to the last element and add the css effect you want....

What you asked is not possible according to me unless you have some way of recognizing the last element (may be the id or the name, again set this up if you are in control)

Upvotes: 0

Related Questions