user4540334
user4540334

Reputation:

document.querySelector exclude part of DOM

My goal is to select h2, h3, h4 heading level elements in DOM except the ones that are inside docs-body__heading class

I tried this:

document.querySelectorAll(`:not(.docs-body__heading) h2, h2, h4`);

Doesn't work. So, how do I use querySelectorAll() on DOM excluding some part of it?

Upvotes: 1

Views: 1792

Answers (4)

Hirad Nikoo
Hirad Nikoo

Reputation: 1629

This one line will give you all you need!

document.querySelectorAll("*:not(.docs-body__heading) > h1, *:not(.docs-body__heading) > h2, *:not(.docs-body__heading) > h4");

Easier to read:

let not = "*:not(.docs-body__heading)";

document.querySelectorAll(`${not} > h1, ${not} > h2, ${not} > h4`);

Sample in use:

window.addEventListener('load', () => {
   var elems = document.querySelectorAll("*:not(.docs-body__heading) > h1, *:not(.docs-body__heading) > h2, *:not(.docs-body__heading) > h4");
   for(var i = 0 ; i < elems.length ; i++) {
      elems[i].style.backgroundColor = "red";
   }
});
<html>
    <head>Selector</head>
    <body>
        <h1>1</h1>
        <h2>2</h2>
        <h4>3</h4>
        <div class="docs-body__heading">
            <h1>1</h1>
            <h2>2</h2>
            <h4>3</h4>
        </div>
    </body>
</html>

Upvotes: 0

Kaiido
Kaiido

Reputation: 136986

The easiest is by first getting all the elements then filtering out the ones that you don't want:

const elems = [...document.querySelectorAll('h2,h4')]
  .filter( (elem) => !elem.matches('.docs-body__heading *') );

console.log(elems);
<div class="docs-body__heading">
  <h2>do not select me</h2>
  <div><h4>me neither</h4></div>
</div>
<div>
  <h2>select me</h2>
  <div><h4>and me</h4></div>
</div>

The fastest (in terms of performances) might be using a TreeWalker:

const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT, {
  acceptNode(elem) {
    return elem.matches('.docs-body__heading') ?
      NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT;
  }
}, true);
const elems = [];
while (walker.nextNode()) {
  const tagname = walker.currentNode.tagName;
  if (tagname === 'H2' || tagname === 'H4') {
    elems.push(walker.currentNode);
  }
}

console.log(elems);
<div class="docs-body__heading">
  <h2>do not select me</h2>
  <div>
    <h4>me neither</h4>
  </div>
</div>
<div>
  <h2>select me</h2>
  <div>
    <h4>and me</h4>
  </div>
</div>

But if you are not doing it in a kilometer long document with millions of DOM nodes, the first version should be largely enough.

Upvotes: 2

naortor
naortor

Reputation: 2089

I think it can be done only if the h2/h4 are direct childs of docs-body__heading

const x = document.querySelectorAll(":not(.docs-body__heading) > h2,h4");

console.log(x);
<div class="docs-body__heading">
  <h2>A</h2>
</div>
  <h4>B</h1>
  <h2>C</h1>

Upvotes: 0

Daniel Geffen
Daniel Geffen

Reputation: 1862

Now you are excluding elements that have the docs-body__heading class, to exclude element that are in it use:

document.querySelectorAll(`:not(.docs-body__heading > *) h2, h2, h4`);

Upvotes: 0

Related Questions