47

I am trying to write a script in vanilla JS (no jQuery) that will remove an element from the page if someone clicks outside of this element.

However, this div has many nested elements and the way I had it set up is that it disappears even by clicking an element that is within the first element.

Example markup:

<div id='parent-node'>
  This is the Master Parent node
  <div id ='not-parent-node'>
     Not Parent Node
    <button>Button</button>
    <div id='grandchild-node'>
      Grandbaby Node
    </div>
  </div>
</div> 

So I would like that no matter how deeply nested an element is, it will check to see if its a descendant of the <div id='parent-node'> element. So if I click there, it will not get rid of the parent node and all its descendants. The div and its descendants should ONLY be removed dynamically when clicking outside of the parent div.

Currently this is what I have and I do know there are some serious fallacies in what I wrote:

function remove(id) {
    return (elem = document.getElementById(id)).parentNode.removeChild(elem);
}

document.addEventListener("click", function (e) {
    remove('parent-node');
});
Josh Crozier
  • 233,099
  • 56
  • 391
  • 304
Dear1ofGdBear
  • 935
  • 2
  • 13
  • 26
  • You're right, this has been asked many, many times. What's wrong the [existing solution](http://stackoverflow.com/questions/152975/how-to-detect-a-click-outside-an-element)? (It's trivial to adapt this from jQuery to vanilla JS) – Evan Davis Jan 05 '16 at 22:06
  • 5
    Why adapt from jQuery? It's always better to learn the JavaScript basics first and **then** one can deal with libraries like jQuery. Especially if someone **wants** to as mentioned in the question! –  Jan 05 '16 at 22:15

2 Answers2

84

Since event.target is a reference to the clicked element, you can check to see if #parent-node is event.target or if it contains event.target as a descendant element.

Example Here

In the snippet below, an event listener is attached to the document. If the element that triggered the click event isn't a descendant of #parent-node and isn't #parent-node, then the element is removed.

document.addEventListener("click", function(e) {
  var element = document.getElementById('parent-node');

  if (e.target !== element && !element.contains(e.target)) {
    element.parentNode.removeChild(element);
  }
});

document.addEventListener("click", function(e) {
  var element = document.getElementById('parent-node');
  
  if (e.target !== element && !element.contains(e.target)) {
    element.parentNode.removeChild(element);
  }
});
#parent-node {
  background-color: #f00;
}
<div id='parent-node'>
  This is the Master Parent node
  <div id='not-parent-node'>
    Not Parent Node
    <button>Button</button>
    <div id='grandchild-node'>
      Grandbaby Node
    </div>
  </div>
</div>
Josh Crozier
  • 233,099
  • 56
  • 391
  • 304
20

You can use Element.matches() to determine if the Event.target has the id #parent-node or if it is a descendant (#parent-node *).

Then, you can use ChildNode.remove() to delete the parent-node element if the condition is false:

document.addEventListener('click', event => {
  if (!event.target.matches('#parent-node, #parent-node *')) {
    document.getElementById('parent-node').remove();
  }
});
Grant Miller
  • 27,532
  • 16
  • 147
  • 165