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>