Using querySelectorAll to retrieve direct children

I am able to do this:

<div id="myDiv">
   <div class="foo"></div>
</div>
myDiv = getElementById("myDiv");
myDiv.querySelectorAll("#myDiv > .foo");

That is, I can successfully retrieve all the direct children of the myDiv element that have class .foo.

The problem is, it bothers me that I must include the #myDiv in the selector, because I am running the query on the myDiv element (so it is obviously redundant).

I ought to be able to leave the #myDiv off, but then the selector is not legal syntax since it starts with a >.

Does anyone know how to write a selector which gets just the direct children of the element that the selector is running on?


Solution 1:

Good question. At the time it was asked, a universally-implemented way to do "combinator rooted queries" (as John Resig called them) did not exist.

Now the :scope pseudo-class has been introduced. It is not supported on [pre-Chrominum] versions of Edge or IE, but has been supported by Safari for a few years already. Using that, your code could become:

let myDiv = getElementById("myDiv");
myDiv.querySelectorAll(":scope > .foo");

Note that in some cases you can also skip .querySelectorAll and use other good old-fashioned DOM API features. For example, instead of myDiv.querySelectorAll(":scope > *") you could just write myDiv.children, for example.

Otherwise if you can't yet rely on :scope, I can't think of another way to handle your situation without adding more custom filter logic (e.g. find myDiv.getElementsByClassName("foo") whose .parentNode === myDiv), and obviously not ideal if you're trying to support one code path that really just wants to take an arbitrary selector string as input and a list of matches as output! But if like me you ended up asking this question simply because you got stuck thinking "all you had was a hammer" don't forget there are a variety of other tools the DOM offers too.

Solution 2:

Does anyone know how to write a selector which gets just the direct children of the element that the selector is running on?

The correct way to write a selector that is "rooted" to the current element is to use :scope.

var myDiv = getElementById("myDiv");
var fooEls = myDiv.querySelectorAll(":scope > .foo");

However, browser support is limited and you'll need a shim if you want to use it. I built scopedQuerySelectorShim for this purpose.