2

Consider that you are given a node node and you must provide all direct children given by selector Direct. Selector for direct child is:

childParent > directChild

However, the following fails with an error in console:

document.body.querySelectorAll(">div")
SyntaxError: '>div' is not a valid selector

I have a function that needs to do something on select direct child nodes, but I'm not sure how to handle this. Except of course using the for loop and analyse the children with my own code, abandoning selectors completely.

The following code does not work. Can it be changed so that it does what is intended?

function doWithDirectChildren(parentNode) {
    // does not work, the selector is invalid
    const children = parentNode.querySelector(">.shouldBeAffected");
    for(const direct of children) {
        // do something with the direct child
    }
}

I'm asking for a solution, not a workaround.

Tomáš Zato
  • 50,171
  • 52
  • 268
  • 778

3 Answers3

7

The proper way to do this is with the :scope psuedo class.

According to the documentation at MDN:

When used from a DOM API such as querySelector(), querySelectorAll(), matches(), or Element.closest(), :scope matches the element on which the method was called.

For example:

let parent = document.querySelector('#parent');
let scoped = parent.querySelectorAll(':scope > span');

Array.from(scoped).forEach(s => {
  s.classList.add('selected');
});
.selected {
  background: yellow;
}
<div id="parent">
  <span> Select Me </span> <br>
  <span> Me Too </span>
</div>
<span> Not Selected </span>
Besworks
  • 4,123
  • 1
  • 18
  • 34
  • Caution, the `:scope` selector is not compatible with the sibling selectors. Example: `:scope + *` or `:scope ~ *` do not work. Question open here: https://stackoverflow.com/questions/65595914/css-pseudo-selector-to-select-the-current-element-within-queryselector-usable-fo – Simon Jul 06 '22 at 13:21
  • 1
    @Simon, for `:scope + *` use [Element.nextElementSibling](https://developer.mozilla.org/en-US/docs/Web/API/Element/nextElementSibling), for `:scope ~ *` do `[...element.parentElement.children].filter((el,i,a) => i>a.indexOf(element));` – Besworks Jul 06 '22 at 14:01
0

The child combinator operator > is a binary operator so using it with nothing on the left side is invalid.

The child combinator (>) is placed between two CSS selectors. It matches only those elements matched by the second selector that are the direct children of elements matched by the first.

If you can provide individual parent and child selectors you can do something simple like this

let directChildren = (parent, child) => document.querySelectorAll(`${parent} > ${child}`);
directChildren('body','div');//...

If your parent argument is a node or collection you would have to use a method of converting it back to a selector, like this one

chiliNUT
  • 18,989
  • 14
  • 66
  • 106
-1

jQuery solves this problem in 2 ways. Consider this code:

$('div.root').find('> .p2').addClass('highlighted');
$('div.root').children('.p2').addClass('red');
.highlighted {
  background: yellow
}

.red {
  color: red
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="root">
  <p>div 1</p>
  <p class="p2">paragraph 2</p>
  <p>paragraph 3</p>
  
  <div>
    <p class="p2">paragraph 2 2</p>
  </div>
</div>

using .find('> selector) finds only direct children matching the selector, and using .children('selector') also does the same.

TKoL
  • 13,158
  • 3
  • 39
  • 73
  • 1
    The question is not tagged `jquery`, IMHO it would be overkill to add JQuery only for this problem. (Furthermore, I think it would be overkill to add it for any problem in 2020. But that's just my opinion) – Ulysse BN Jan 09 '20 at 18:31
  • Most of my projects use React and so have no need for any dom-selecting logic. But there's no doubt in my mind that if you need complicated dom manipulation, jQuery still offers the best api, much much better than the tools provided by browsers by default (even with es6 features) – TKoL Jan 10 '20 at 09:38