0

Let's say I have an element and I want to select the next matching sibling. Normally, in css I'd be able to write <some selector for this element> ~ <selector for sibling>, but the sibling combinators (~ and +) don't seem to work when applied inside querySelector with the :scope pseudo-selector.

Here's an example

<div id="container">
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item active"></div>
  <div class="item"></div>
  <div class="item"></div>
</div>
document.addEventListener("DOMContentLoaded", () => {
  const container = document.getElementById('container');

  // this is my element that I have and I want to get the next .active
  const item = container.querySelector('.item:nth-child(2)');

  // this doesn't work
  const nextActiveFromSibling = item.querySelector(':scope ~ .active');
  console.log(`From sibling success: ${nextActiveFromSibling !== null}`);

  // this works but requires me to know how to select the original item from the parent
  const nextActiveFromParent = container.querySelector('.item:nth-child(2) ~ .active');
  console.log(`From parent success: ${nextActiveFromParent !== null}`);
});

prints:

From sibling success: false
From parent success: true

Why doesn't :scope ~ .active work when applied to the selected item?

3
  • 1
    because .querySelector selects descendants of the element you use it on Commented Jul 10 at 6:46
  • Oh, I see. So is there any way to select a matching sibling in JavaScript other than iterating over the siblings and checking each one? Commented Jul 10 at 6:56
  • As @JaromandaX mentioned querySelector only shows the descendants of the element for getting an element's siblings in vanilla js I don't know but with JQuery you can use siblings() method check this link Commented Jul 10 at 7:02

1 Answer 1

0

In CSS :scope ~ .active combination works as "select element with the class 'active' that is a sibling of the parent element represented by :scope.

However, when used in JavaScript's querySelector method, the :scope pseudo-class doesn't work as expected because it represents the context of the function, not the current element. That's why the combination :scope ~ .active doesn't work.

Here's how you can rewrite the selector without using :scope:

document.addEventListener("DOMContentLoaded", () => {
  const container = document.getElementById('container');

  // this is my element that I have and I want to get the next .active
  const item = container.querySelector('.item:nth-child(2)');

  // find the next sibling element with class 'active'
  const parentElement = item.parentElement;
  const nextActiveFromParent = parentElement.querySelector('.item:nth-child(2) ~ .active');

  console.log(`From parent success: ${nextActiveFromParent !== null}`);
});
<div id="container">
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item active"></div>
  <div class="item"></div>
  <div class="item"></div>
</div>

I am available to you if you need further assistance.

Thanks!

1
  • Hi and thank you for the explanation. However, in your example you still use .item:nth-child(2) to refer to the item element I had, but in general the selector for it wouldn't be known. I just used this as a dummy example to show that ~ works from the parent's context. Imagine if I had several .active items and my JavaScript code wanted to select the next .active relative to some HTMLElement object, and the structure could be complex. I wouldn't be able to write a selector for the HTMLElement object that I have. I hope this makes sense. Commented Jul 10 at 7:23

Not the answer you're looking for? Browse other questions tagged or ask your own question.