Skip to content
Advertisement

How to check if some DOM element in another DOM element tree?

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?

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:

  1. jQuery supports event delegation, so it does the checks for you. But I’m not immediately sure that applies to what you’re doing.

  2. 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.

Advertisement