skip to Main Content

Using CSS only, I would like to select the last 4 elements, but only when any of them has the "selected" class.

In the snippet you can see in the second row how i’d like it to work.

let sel=0;
setTimeout(setSelected, 1000);

function setSelected() {
  document.getElementById("d"+sel).classList.remove("selected");
  document.getElementById("j"+sel).classList.remove("selected");

  if (++sel>5) sel = 0;
  document.getElementById("d"+sel).classList.add("selected");

  // This is only to show what i would like to get with CSS selectors
  document.getElementById("j"+sel).classList.add("selected");
  if (sel>=3) {
    document.getElementById("j3").classList.add("active");
    document.getElementById("j4").classList.add("active");
    document.getElementById("j5").classList.add("active");
  }else{
    document.getElementById("j3").classList.remove("active");
    document.getElementById("j4").classList.remove("active");
    document.getElementById("j5").classList.remove("active");
  }
  setTimeout(setSelected, 1000);
}
.container {text-align: center;}
.normal, .example {
  display: inline-block;
  width: 3rem;
  color: white;
  background-color: #004;
  text-align: center;
  border: 2px solid transparent;
}
.normal:last-of-type,
.normal:nth-last-of-type(2),
.normal:nth-last-of-type(3)
{
  background-color: #A00;
}
.selected{
  background-color: #0A0;
  border: 2px solid #0A0;
}
.active{background-color: #A00;}
<p>CSS Only</p>
<div class="container">
  <div id="d0" class="normal selected">0</div>
  <div id="d1" class="normal">1</div>
  <div id="d2" class="normal">2</div>
  <div id="d3" class="normal">3</div>
  <div id="d4" class="normal">4</div>
  <div id="d5" class="normal">5</div>
</div>
<p>example with Javascript</p>
<div class="container">
  <div id="j0" class="example selected">0</div>
  <div id="j1" class="example">1</div>
  <div id="j2" class="example">2</div>
  <div id="j3" class="example">3</div>
  <div id="j4" class="example">4</div>
  <div id="j5" class="example">5</div>
</div>

I started by selection the last of them:

.normal:last-of-type,
.normal:nth-last-of-type(2),
.normal:nth-last-of-type(3)
{
  background-color: #A00;
}

but then i wasn’t able to go further; by reading the description of CSS :has() i have the impression that a clever combination of it and nth-last-of-type() could get me there, but i can’t figure out how should that "stack" of selectors be built.

Is it possible, using only CSS, to conditionally select the last 3 items only if one of them has the "selected" class?
If yes, how?

-EDIT-
Since the question was close ’cause seems to be a duplicate of Can I combine :nth-child() or :nth-of-type() with an arbitrary selector? :

My problem is that i need to select ALL of the last three divs when only one of them match an arbitrary selector, while in the linked question all of the selected elements matches the selector.

Trying to say it in another way, i need to select the elements 3, 4 and 5 when either 3 or 4 or 5 has the "selected" class.

2

Answers


  1. You can try .container:has(.selected:nth-last-of-type(-n + 3)) > :nth-last-of-type(-n + 3). When the container has an element with .selected and that element is one of the last 3, select the last 3

    let sel=0;
    setTimeout(setSelected, 1000);
    
    function setSelected() {
      document.getElementById("d"+sel).classList.remove("selected");
      document.getElementById("j"+sel).classList.remove("selected");
    
      if (++sel>5) sel = 0;
      document.getElementById("d"+sel).classList.add("selected");
    
      // This is only to show what i would like to get with CSS selectors
      document.getElementById("j"+sel).classList.add("selected");
      if (sel>=3) {
        document.getElementById("j3").classList.add("active");
        document.getElementById("j4").classList.add("active");
        document.getElementById("j5").classList.add("active");
      }else{
        document.getElementById("j3").classList.remove("active");
        document.getElementById("j4").classList.remove("active");
        document.getElementById("j5").classList.remove("active");
      }
      setTimeout(setSelected, 1000);
    }
    .container {text-align: center;}
    .normal, .example {
      display: inline-block;
      width: 3rem;
      color: white;
      background-color: #004;
      text-align: center;
      border: 2px solid transparent;
    }
    
    .container:has(.selected:nth-last-of-type(-n + 3)) > :nth-last-of-type(-n + 3) {
      background-color: #A00;
    }
    .selected{
      background-color: #0A0;
      border: 2px solid #0A0;
    }
    .active{background-color: #A00;}
    <p>CSS Only</p>
    <div class="container">
      <div id="d0" class="normal selected">0</div>
      <div id="d1" class="normal">1</div>
      <div id="d2" class="normal">2</div>
      <div id="d3" class="normal">3</div>
      <div id="d4" class="normal">4</div>
      <div id="d5" class="normal">5</div>
    </div>
    <p>example with Javascript</p>
    <div class="container">
      <div id="j0" class="example selected">0</div>
      <div id="j1" class="example">1</div>
      <div id="j2" class="example">2</div>
      <div id="j3" class="example">3</div>
      <div id="j4" class="example">4</div>
      <div id="j5" class="example">5</div>
    </div>
    Login or Signup to reply.
  2. It can be done with a combination of nth-last-of-type() and :has.

    /* For demo. Set .selected on click */
    const items = document.querySelectorAll(".container > div");
    items.forEach(item => {
      item.addEventListener('click', onclick);
    });
    
    function onclick(){
        document.querySelector(".selected").classList.remove("selected")
       this.classList.add("selected");
    }
    .selected {
      outline:2px dotted;
    }
    /* Test if relational pseudo-classes are supported. At the time of writing Firefox is lagging behind. */
    @supports selector(:has(*)) {
      /* 
      If the last nth is selected
      or HAS a subsequent sibling which is selected
      then highlight it and the siblings: 
      */
      .container div:nth-last-of-type(3):is(.selected),
      .container div:nth-last-of-type(3):has(~.selected),
      .container div:nth-last-of-type(3):is(.selected) ~ div,
      .container div:nth-last-of-type(3):has(~.selected) ~ div {
        background: green;
      }
    }
    click an item:
    <div class="container">
      <div id="d0" class="normal selected">0</div>
      <div id="d1" class="normal">1</div>
      <div id="d2" class="normal">2</div>
      <div id="d3" class="normal">3</div>
      <div id="d4" class="normal">4</div>
      <div id="d5" class="normal">5</div>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search