Detect click outside element (vanilla JavaScript)

Javascript

Javascript Problem Overview


I have searched for a good solution everywhere, yet I can't find one which does not use jQuery.

Is there a cross-browser, normal way (without weird hacks or easy to break code), to detect a click outside of an element (which may or may not have children)?

Javascript Solutions


Solution 1 - Javascript

Add an event listener to document and use Node.contains() to find whether the target of the event (which is the inner-most clicked element) is inside your specified element. It works even in IE5

var specifiedElement = document.getElementById('a');

//I'm using "click" but it works with any event
document.addEventListener('click', function(event) {
  var isClickInside = specifiedElement.contains(event.target);

  if (!isClickInside) {
    //the click was outside the specifiedElement, do something
  }
});

var specifiedElement = document.getElementById('a');

//I'm using "click" but it works with any event
document.addEventListener('click', function(event) {
  var isClickInside = specifiedElement.contains(event.target);
  if (isClickInside) {
    alert('You clicked inside A')
  } else {
    alert('You clicked outside A')
  }
});

div {
  margin: auto;
  padding: 1em;
  max-width: 6em;
  background: rgba(0, 0, 0, .2);
  text-align: center;
}

Is the click inside A or outside?
<div id="a">A
  <div id="b">B
    <div id="c">C</div>
  </div>
</div>

Solution 2 - Javascript

You need to handle the click event on document level. In the event object, you have a target property, the inner-most DOM element that was clicked. With this you check itself and walk up its parents until the document element, if one of them is your watched element.

See the example on jsFiddle

document.addEventListener("click", function (e) {
  var level = 0;
  for (var element = e.target; element; element = element.parentNode) {
    if (element.id === 'x') {
      document.getElementById("out").innerHTML = (level ? "inner " : "") + "x clicked";
      return;
    }
    level++;
  }
  document.getElementById("out").innerHTML = "not x clicked";
});

As always, this isn't cross-bad-browser compatible because of addEventListener/attachEvent, but it works like this.

A child is clicked, when not event.target, but one of it's parents is the watched element (i'm simply counting level for this). You may also have a boolean var, if the element is found or not, to not return the handler from inside the for clause. My example is limiting to that the handler only finishes, when nothing matches.

Adding cross-browser compatability, I'm usually doing it like this:

var addEvent = function (element, eventName, fn, useCapture) {
  if (element.addEventListener) {
    element.addEventListener(eventName, fn, useCapture);
  }
  else if (element.attachEvent) {
    element.attachEvent(eventName, function (e) {
      fn.apply(element, arguments);
    }, useCapture);
  }
};

This is cross-browser compatible code for attaching an event listener/handler, inclusive rewriting this in IE, to be the element, as like jQuery does for its event handlers. There are plenty of arguments to have some bits of jQuery in mind ;)

Solution 3 - Javascript

How about this:

jsBin demo

document.onclick = function(event){
  var hasParent = false;
	for(var node = event.target; node != document.body; node = node.parentNode)
	{
	  if(node.id == 'div1'){
		hasParent = true;
		break;
	  }
	}
  if(hasParent)
	alert('inside');
  else
	alert('outside');
} 

Solution 4 - Javascript

I did a lot of research on it to find a better method. JavaScript method .contains go recursively in DOM to check whether it contains target or not. I used it in one of react project but when react DOM changes on set state, .contains method does not work. SO i came up with this solution

//Basic Html snippet
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
<div id="mydiv">
  <h2>
    click outside this div to test
  </h2>
  Check click outside 
</div>
</body>
</html>


//Implementation in Vanilla javaScript
const node = document.getElementById('mydiv')
//minor css to make div more obvious
node.style.width = '300px'
node.style.height = '100px'
node.style.background = 'red'

let isCursorInside = false

//Attach mouseover event listener and update in variable
node.addEventListener('mouseover', function() {
  isCursorInside = true
  console.log('cursor inside')
})

/Attach mouseout event listener and update in variable
node.addEventListener('mouseout', function() {
	isCursorInside = false
  console.log('cursor outside')
})


document.addEventListener('click', function() {
  //And if isCursorInside = false it means cursor is outside 
	if(!isCursorInside) {
  	alert('Outside div click detected')
  }
})

WORKING DEMO jsfiddle

Solution 5 - Javascript

Another very simple and quick approach to this problem is to map the array of path into the event object returned by the listener. If the id or class name of your element matches one of those in the array, the click is inside your element.

(This solution can be useful if you don't want to get the element directly (e.g: document.getElementById('...'), for example in a reactjs/nextjs app, in ssr..).

Here is an example:

   document.addEventListener('click', e => {
      let clickedOutside = true;

      e.path.forEach(item => {
        if (!clickedOutside)
          return;

        if (item.className === 'your-element-class')
          clickedOutside = false;
      });

      if (clickedOutside)
        // Make an action if it's clicked outside..
    });

I hope this answer will help you ! (Let me know if my solution is not a good solution or if you see something to improve.)

Solution 6 - Javascript

With the following code you can detect a click outside an element, I used it to close a modal only when clicked outside an element.

 document.addEventListener("click", (e) => { 
    if(e.target === document.body){
       console.log("Click outside of an element");
 });

Solution 7 - Javascript

To hide element by click outside of it I usually apply such simple code:

var bodyTag = document.getElementsByTagName('body');
var element = document.getElementById('element'); 
function clickedOrNot(e) {
	if (e.target !== element) {
		// action in the case of click outside 
		bodyTag[0].removeEventListener('click', clickedOrNot, true);
	}	
}
bodyTag[0].addEventListener('click', clickedOrNot, true);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTiberiu-IonuČ› StanView Question on Stackoverflow
Solution 1 - JavascriptfreganteView Answer on Stackoverflow
Solution 2 - JavascriptmetadingsView Answer on Stackoverflow
Solution 3 - JavascriptAkhil SekharanView Answer on Stackoverflow
Solution 4 - JavascriptAmrendra KumarView Answer on Stackoverflow
Solution 5 - JavascripthpapierView Answer on Stackoverflow
Solution 6 - JavascriptGabriela CView Answer on Stackoverflow
Solution 7 - JavascriptAleks SidView Answer on Stackoverflow