105

I'd like to detect a click inside or outside a div area. The tricky part is that the div will contain other elements and if one of the elements inside the div is clicked, it should be considered a click inside, the same way if an element from outside the div is clicked, it should be considered an outside click.

I've been researching a lot but all I could find were examples in jquery and I need pure javascript.

Any suggestion will be appreciated.

  • 1
    http://stackoverflow.com/questions/152975/how-to-detect-a-click-outside-an-element?rq=1 and then just change the jquery click bindings for javascript click bindings. – An0nC0d3r Apr 18 '16 at 13:32
  • What examples did you find that were using jQuery? Perhaps you should just try to "translate" those to vanilla JS and ask about that if you don't know how. – noppa Apr 18 '16 at 13:32
  • you can call the function to be called from inline onclick function of the HTML element `
    `
    – Kishore Barik Apr 18 '16 at 13:34

9 Answers9

240

It depends on the individual use case but it sounds like in this example there are likely to be other nested elements inside the main div e.g. more divs, lists etc. Using Node.contains would be a useful way to check whether the target element is within the div that is being checked.

window.addEventListener('click', function(e){   
  if (document.getElementById('clickbox').contains(e.target)){
    // Clicked in box
  } else{
    // Clicked outside the box
  }
});

An example that has a nested list inside is here.

James Bubb
  • 3,023
  • 1
  • 12
  • 12
  • 3
    `if (!document.getElementById('clickbox').contains(e.target)){ // Clicked outside box }` is perhaps a working and shorter solution? – 6754534367 May 29 '19 at 01:16
  • 4
    This is working but not perfectly. It is not working when clicking an inside 'absolute' positioned element. Can we handle that case? – benshabatnoam Aug 09 '20 at 07:01
  • 4
    instead of using `window.addEventListener` use `document.addEventListener` it gonna do the work , especially in js frameworks (vue,react and angular) – Clarance Liberiste Ntwari Sep 04 '20 at 13:31
6

You can check if the clicked Element is the div you want to check or not:

document.getElementById('outer-container').onclick = function(e) {
  if(e.target != document.getElementById('content-area')) {
      console.log('You clicked outside');
  } else {
      console.log('You clicked inside');
  }
}

Referring to Here.

Community
  • 1
  • 1
mohamed-ibrahim
  • 10,837
  • 4
  • 39
  • 51
  • 2
    For what it's worth - you're far better off adding the listener using `.addEventListener`- you can remove it, but more importantly, you can set a number of functions to fire when the event is received. You also gain meaningful access to the `this` keyword inside the function that handles the event, `this` then resolves to the element itself - nice to know which element triggered the event, for instance.You also get the event object, which triggered the handler in the first place. In any case, the method of attaching the event handlers is considerably more powerful. :) – enhzflep Apr 18 '16 at 13:40
3

you can apply if check for that inside your click event

if(event.target.parentElement.id == 'yourID')
usman khan
  • 119
  • 5
3
  closePopover () {
var windowBody = window
var popover = document.getElementById('popover-wrapper') as HTMLDivElement; 
windowBody?.addEventListener('click', function(event){ 
    if(popover === event.target) {
      console.log("clicked on the div")
    }
    if(popover !== event.target) {
      console.log("clicked outside the div")
    }
})

} }

Joseph Owigo
  • 430
  • 3
  • 10
2

In Angular 6 and IONIC 3, I do same as here:

import {Component} from 'angular/core';

@Component({
  selector: 'my-app',
  template: `
    <ion-content padding (click)="onClick($event)">
      <div id="warning-container">
      </div>
    </ion-content>
  `
})
export class AppComponent {
  onClick(event) {
    var target = event.target || event.srcElement || event.currentTarget;
    if (document.getElementById('warning-container').contains(target)){
      // Clicked in box
    } else{
      // Clicked outside the box
    }
  }
}

This working fine on web/android/ios. It might be helpful for someone, Thanks.

Kamlesh Kumar
  • 1,632
  • 2
  • 21
  • 31
1

Try this solution it uses pure javascript and it solves your problem. I added css just for better overview... but it is not needed.

document.getElementById('outer-div').addEventListener('click', function(){
  alert('clicked outer div...');
});

document.getElementById('inner-div').addEventListener('click', function(e){
  e.stopPropagation()
  alert('clicked inner div...');
});
#outer-div{
  width: 200px;
  height: 200px;
  position: relative;
  background: black;
}

#inner-div{
  top: 50px;
  left: 50px;
  width: 100px;
  height: 100px;
  position: absolute;
  background: red;
}
<div id="outer-div">
  <div id="inner-div">
  </div>
</div>
Kingmaker
  • 469
  • 1
  • 6
  • 20
1

I came up with a hack for this that's working well for me and that might help others.

When I pop up my dialog DIV, I simultaneously display another transparent DIV just behind it, covering the whole screen.

This invisible background DIV closes the dialog DIV onClick.

This is pretty straightforward, so I'm not going to bother with the code here. LMK in the comments if you want to see it and I'll add it in.

HTH!

nbardach
  • 123
  • 1
  • 10
  • it might be straightforward for experts. But so far of all the solutions posted this might be what I need as I have a div table that displays an info when clicked, and don't think I'm confident with the addEventListeners. Yours might be a good way for me. Curious how would I create an invisible background DIV around the displayedDiv – Azrudi Aug 28 '20 at 03:50
  • Something like this
    content
    . HTH!
    – nbardach Aug 28 '20 at 07:08
  • Completely unhelpful IMO. – glennstar Sep 03 '22 at 19:02
1

I recently needed a simple vanilla JS solution which solves for:

  • Ignoring specific selectors including whether a parent contains one of these selectors
  • Ignoring specific DOM nodes

This solution has worked quite well in my app.

const isClickedOutsideElement = ({ clickEvent, elToCheckOutside, ignoreElems = [], ignoreSelectors = [] }) => {
    const clickedEl = clickEvent.srcElement;
    const didClickOnIgnoredEl = ignoreElems.filter(el => el).some(element => element.contains(clickedEl) || element.isEqualNode(clickedEl));
    const didClickOnIgnoredSelector = ignoreSelectors.length ? ignoreSelectors.map(selector => clickedEl.closest(selector)).reduce((curr, accumulator) => curr && accumulator, true) : false;

    if (
      isDOMElement(elToCheckOutside) &&
      !elToCheckOutside.contains(clickedEl) &&
      !didClickOnIgnoredEl &&
      !didClickOnIgnoredSelector
    ){
      return true;
    }

    return false;
}

  const isDOMElement = (element) => {
    return element instanceof Element || element instanceof HTMLDocument;  
  }
dipole_moment
  • 5,266
  • 4
  • 39
  • 55
1

In React you can use useClickOutside hook from react-cool-onclickoutside.

Demo from Github:

import { useClickOutside } from 'use-events';

const Example = () => {
  const ref1 = React.useRef(null);
  const ref2 = React.useRef(null);
  const [isActive] = useClickOutside([ref1, ref2], event => console.log(event));

  return (
    <div>
      <div ref={ref1} style={{ border: '1px dotted black' }}>
        You are {isActive ? 'clicking' : 'not clicking'} outside of this div
      </div>
      <br />
      <div ref={ref2} style={{ border: '1px dotted black' }}>
        You are {isActive ? 'clicking' : 'not clicking'} outside of this div
      </div>
    </div>
  );
};

Live demo

Jakub Słowikowski
  • 1,063
  • 1
  • 8
  • 13