cloakedninjas cloakedninjas - 2 months ago 14
HTML Question

document.querySelector direct descendant issue

Given the following HTML:

<ul class="menu">
<li class="active">
Books
<ul>
<li>See All</li>
<li>
Crime
<ul class="expected">
<li>Agatha Cristie</li>
<li>Tom Clancy</li>
</ul>
</li>
<li>
Cooking
<ul>
<li>Delia Smith</li>
<li>Jamie Oliver</li>
</ul>
</li>
</ul>
</li>
<li>
Electronics
<ul>
<li>See All</li>
<li>TVs</li>
</ul>
</ul>
</li>
</ul>


and using this selector:

var activeItem = document.querySelector('.active');
var nestedMenu = activeItem.querySelector('ul > li > ul');

nestedMenu.classList.add('actual');


Can someone explain why the selector
ul > li > ul
isn't getting a third-depth
ul.expected
, but the top-level
ul.menu
?

Fiddle here: https://jsfiddle.net/dczat6g7/

This seems to be an invalid result, I would expect either the
.expected
UL to be found or null. Am I missing something?

Answer

From the MDN docs for querySelector:

Returns the first element that is a descendant of the element on which it is invoked that matches the specified group of selectors.

This does not say that the selector is applied starting at the designated not, only that the results of the selector are filtered to descendent of the designated node.

Ie. it returns the first node which is both in the set of nodes matched by the selector – globally – and is a descendant of activeItem.

And the one node that gets .actual applied meets both criteria.

There is no universal – across all browsers – CSS selector method that will operate relative to another node. For non-IE you can include :scope in your CSS to operate relative to the designated elements. A universal solution would be to use an id selector. So you could add an id to your starting node (or use its existing id) and then include that id in the CSS selector.