How to check if some DOM element in another DOM element tree?
For example to hide menu when you click on main page content instead of menu you can:
document.addEventListener(function (e) { var node = e.target; do { if (node.classList.contains('menu-area')) return; node = node.parentNode; } while (node instanceof HTMLElement); closeMenu(); });
Note that usual solution to hide menu when you click to non-menu area is event.stopPropagation()
on menu and non-conditional document.addEventListener()
.
I make test code with iteration loop over node = node.parentNode
and ===
operator:
<style> div { margin: 20px; padding: 5px; border: 1px green dotted; } </style> <div id="lvl1"> <div id="lvl2"> <div id="lvl3"> Click me </div> </div> </div> <div id="result"></div> <script> var lvl1Node = document.getElementById('lvl1'); document.addEventListener('click', function(e) { var node = e.target; do { if (lvl1Node === node) { document.getElementById('result').innerHTML = "Event from: " + e.target.id; return; } node = node.parentNode; } while (node instanceof HTMLElement); }); </script>
So only click inside <div id='lvl1'>
change <div id="result">
area. Is that right solution (according to standard)?
That jQuery/Backbone/Underscore/Mojo/etc have to this?
Advertisement
Answer
I wouldn’t use instanceof
for this (not least because it won’t work in IE8, which sadly continues to linger). Test for ===
the node where you want to stop, probably document.body
or document.documentElement
(the <html>
element):
document.addEventListener("click", function (e) { var node = e.target; do { if (node.classList.contains('menu-area')) return; node = node.parentNode; } while (node !== document.documentElement); closeMenu(); });
Or as your loop has initialization, test, and “increment,” it’s a good match for for
:
document.addEventListener("click", function (e) { var node; for (node = e.target; node !== document.documentElement; node = node.parentNode) { if (node.classList.contains('menu-area')) { return; } } closeMenu(); });
That jQuery/Backbone/Underscore/Mojo/etc have to this?
For what you’re doing above, jQuery does, yes, in two ways:
jQuery supports event delegation, so it does the checks for you. But I’m not immediately sure that applies to what you’re doing.
If for some reason you couldn’t use that, jQuery’s
closest
would probably help:if (!$(e.target).closest(".menu-area")[0]) { closeMenu(); }
closest
tests the element against a selector and, if it doesn’t match, checks its parent node, and so on. The[0]
at the end tells us whether jQuery found anything. In the above, if it didn’t find anything (the!
at the beginning), we close the menu.