Home > Mobile >  Select css siblings between two elements
Select css siblings between two elements

Time:04-13

The html I'm looking at looks like this:

document.querySelectorAll('h4#unique_id  ~ h5 ~p  p  h5').forEach(title => console.log(title))
<body>
  <h4 id="unique_id"> foo bing bar </h4>
  <p>some text</p>
  <p>some text</p>
  <p>some text</p>
  <!-- selection should start here -->
    <h5>title text 1</h5>
    <p>some text</p>
    <p>some text</p>
    <p>some text</p>
    <h5>title text 2</h5>
    <p>some text</p>
    <p>some text</p>
    <p>some text</p>
    <p>some text</p>
    <h5>title text 3</h5>
    <p>some text</p>
    <p>some text</p>
    <p>some text</p>
    <p>some text</p>
    <p>some text</p>
    <p>some text</p>
  <!-- and end here -->
  <h4 id="randome_id_234353">title text</h4>
  <p>some text I don't want</p>
  <p>some text I don't want</p>
  <h5>title text I don't want</h5>
  <p>some text I don't want</p>
  <h5>title text I don't want</h5>
  <p>some text I don't want</p>
  <h5>title text I don't want</h5>
  <p>some text I don't want</p>
</body>

I want to grab all the h5 elements, and if possible the p elements as well.

I have document.querySelectorAll('h4#unique_id ~ h5 ~p p h5') so far but that misses the first h5 I want and grabs the last one I don't want. I can't figure out sibling selectors or how to use :not or similar to stop when I find another h4.

This question Select all elements between two known elements by CSS selector relies on jquery which I don't have access to, and Select elements between two defined elements relies on knowning the number and position of elements, which I don't.

Is this possible with just css selectors?

CodePudding user response:

Ok you can use a mixture of sibling selector and not:

The selector is:

#unique_id~p:not(#unique_id~h4~p) /* any p following the unique id but not a p following another h4 after the unique id */

you then repeat this selector for your h5s and the result is:

document.querySelectorAll('#unique_id~p:not(#unique_id~h4~p),#unique_id~h5:not(#unique_id~h4~h5)').forEach(title => title.style.color = 'red');
<h4 id="unique_id"> foo bing bar </h4>
<p>some text</p>
<p>some text</p>
<p>some text</p>
<h5>title text 1</h5>
<p>some text</p>
<p>some text</p>
<p>some text</p>
<h5>title text 2</h5>
<p>some text</p>
<p>some text</p>
<p>some text</p>
<p>some text</p>
<h5>title text 3</h5>
<p>some text</p>
<p>some text</p>
<p>some text</p>
<p>some text</p>
<p>some text</p>
<p>some text</p>
<h4 id="randome_id_234353">title text</h4>
<p>some text I don't want</p>
<p>some text I don't want</p>
<h5>title text I don't want</h5>
<p>some text I don't want</p>
<h5>title text I don't want</h5>
<p>some text I don't want</p>
<h5>title text I don't want</h5>
<p>some text I don't want</p>

As per your edit - not to include the p before the first h5 (selector is slightly modified from Alohci's comment):

document.querySelectorAll('#unique_id~:is(h5, h5~p):not(#unique_id ~ h4 ~ *)').forEach(title => title.style.color = 'red');
<h4 id="unique_id"> foo bing bar </h4>
<p>some text</p>
<p>some text</p>
<p>some text</p>
<h5>title text 1</h5>
<p>some text</p>
<p>some text</p>
<p>some text</p>
<h5>title text 2</h5>
<p>some text</p>
<p>some text</p>
<p>some text</p>
<p>some text</p>
<h5>title text 3</h5>
<p>some text</p>
<p>some text</p>
<p>some text</p>
<p>some text</p>
<p>some text</p>
<p>some text</p>
<h4 id="randome_id_234353">title text</h4>
<p>some text I don't want</p>
<p>some text I don't want</p>
<h5>title text I don't want</h5>
<p>some text I don't want</p>
<h5>title text I don't want</h5>
<p>some text I don't want</p>
<h5>title text I don't want</h5>
<p>some text I don't want</p>

CodePudding user response:

Interesting question. If I understand your problem correctly, you want to log all of the elements that are not containing some text I don't want and the h5-elements.

If you have a parent element, you can use the > to selector in combination with the parent element to select all of its children with the *-selector.

You can apply multiple :not()-selectors to a statement. Below I just selected all p and h4-elements that are preceded by the h4#randome_id_234353-element, like so:

document.querySelectorAll('.container > *:not(h4#randome_id_234353 ~ p):not(h4#randome_id_234353 ~ h5):not(h4)').forEach(title => console.log(title))
<body>
<div >
  <h4 id="unique_id"> foo bing bar </h4>
  <p>some text</p>
  <p>some text</p>
  <p>some text</p>
  <h5>title text 1</h5>
  <p>some text</p>
  <p>some text</p>
  <p>some text</p>
  <h5>title text 2</h5>
  <p>some text</p>
  <p>some text</p>
  <p>some text</p>
  <p>some text</p>
  <h5>title text 3</h5>
  <p>some text</p>
  <p>some text</p>
  <p>some text</p>
  <p>some text</p>
  <p>some text</p>
  <p>some text</p>
  <h4 id="randome_id_234353">title text</h4>
  <p>some text I don't want</p>
  <p>some text I don't want</p>
  <h5>title text I don't want</h5>
  <p>some text I don't want</p>
  <h5>title text I don't want</h5>
  <p>some text I don't want</p>
  <h5>title text I don't want</h5>
  <p>some text I don't want</p>
</div>
</body>

If you don't have a container, you can use the same logic to log everything that does not preceed h4#randome_id_234353.

document.querySelectorAll('h4#unique_id ~ *:not(h4#randome_id_234353 ~ *):not(h4#randome_id_234353)').forEach(title => console.log(title))
<body>
  <h4 id="unique_id"> foo bing bar </h4>
  <p>some text</p>
  <p>some text</p>
  <p>some text</p>
  <h5>title text 1</h5>
  <p>some text</p>
  <p>some text</p>
  <p>some text</p>
  <h5>title text 2</h5>
  <p>some text</p>
  <p>some text</p>
  <p>some text</p>
  <p>some text</p>
  <h5>title text 3</h5>
  <p>some text</p>
  <p>some text</p>
  <p>some text</p>
  <p>some text</p>
  <p>some text</p>
  <p>some text</p>
  <h4 id="randome_id_234353">title text</h4>
  <p>some text I don't want</p>
  <p>some text I don't want</p>
  <h5>title text I don't want</h5>
  <p>some text I don't want</p>
  <h5>title text I don't want</h5>
  <p>some text I don't want</p>
  <h5>title text I don't want</h5>
  <p>some text I don't want</p>
</body>

CodePudding user response:

Only a small number of array methods work with array-like objects, so I often find it useful to turn a NodeList (which is an iterable, array-like object but, crucially, not an array) into an array, so that I can do many more things with it.

In this case by turning NodeList into an array I can use the javascript method .filter() which is exactly what I need to remove the unwanted elements from the end of the collection.


Turning a NodeList into an array:

How can I turn this static NodeList:

let myClassElements = document.querySelectorAll('.myclass');

into an array?

By combining spread syntax (...) with an array literal ([]):

let myClassElements = [... document.querySelectorAll('.myclass')];

On that basis, I can create an array of all the elements I do need (plus a few unnecessary ones):

[...document.querySelectorAll('h4#unique_id ~ h5, h4#unique_id ~ p')]

and also another array of all the unnecessary elements:

[...document.querySelectorAll('h4:not(#unique_id) ~ h5, h4:not(#unique_id) ~ p')]

And then I can .filter() out the latter from the former:

const rawListOfElements = [...document.querySelectorAll('h4#unique_id ~ h5, h4#unique_id ~ p')];

const elementsToRemove = [...document.querySelectorAll('h4:not(#unique_id) ~ h5, h4:not(#unique_id) ~ p')];

const mySelectedElements = rawListOfElements.filter((element) => elementsToRemove.includes(element) === false).forEach(title => console.log(title));

Working Example:

const rawListOfElements = [...document.querySelectorAll('h4#unique_id ~ h5, h4#unique_id ~ p')];

const elementsToRemove = [...document.querySelectorAll('h4:not(#unique_id) ~ h5, h4:not(#unique_id) ~ p')];

const mySelectedElements = rawListOfElements.filter((element) => elementsToRemove.includes(element) === false).forEach(title => console.log(title));
<body>
  <h4 id="unique_id"> foo bing bar </h4>
  <p>some text</p>
  <p>some text</p>
  <p>some text</p>
  <!-- selection should start here -->
    <h5>title text 1</h5>
    <p>some text</p>
    <p>some text</p>
    <p>some text</p>
    <h5>title text 2</h5>
    <p>some text</p>
    <p>some text</p>
    <p>some text</p>
    <p>some text</p>
    <h5>title text 3</h5>
    <p>some text</p>
    <p>some text</p>
    <p>some text</p>
    <p>some text</p>
    <p>some text</p>
    <p>some text</p>
  <!-- and end here -->
  <h4 id="randome_id_234353">title text</h4>
  <p>some text I don't want</p>
  <p>some text I don't want</p>
  <h5>title text I don't want</h5>
  <p>some text I don't want</p>
  <h5>title text I don't want</h5>
  <p>some text I don't want</p>
  <h5>title text I don't want</h5>
  <p>some text I don't want</p>
</body>

  • Related