By Sergio del Amo


2008-09-30 13:17:12 8 Comments

I have some HTML menus, which I show completely when a user clicks on the head of these menus. I would like to hide these elements when the user clicks outside the menus' area.

Is something like this possible with jQuery?

$("#menuscontainer").clickOutsideThisElement(function() {
    // Hide the menus
});

30 comments

@online Thomas 2020-09-28 13:53:11

All of these answers solve the problem, but I would like to contribute with a moders es6 solution that does exactly what is needed. I just hope to make someone happy with this runnable demo.

window.clickOutSide = (element, clickOutside, clickInside) => {
  document.addEventListener('click', (event) => {
    if (!element.contains(event.target)) {
      if (typeof clickInside === 'function') {
        clickOutside();
      }
    } else {
      if (typeof clickInside === 'function') {
        clickInside();
      }
    }
  });
};

window.clickOutSide(document.querySelector('.block'), () => alert('clicked outside'), () => alert('clicked inside'));
.block {
  width: 400px;
  height: 400px;
  background-color: red;
}
<div class="block"></div>

@Mu-Tsun Tsai 2020-08-27 13:30:42

Still looking for that perfect solution for detecting clicking outside? Look no further! Introducing Clickout-Event, a package that provides universal support for clickout and other similar events, and it works in all scenarios: plain HTML onclickout attributes, .addEventListener('clickout') of vanilla JavaScript, .on('clickout') of jQuery, v-on:clickout directives of Vue.js, you name it. As long as a front-end framework internally uses addEventListener to handle events, Clickout-Event works for it. Just add the script tag anywhere in your page, and it simply works like magic.

HTML attribute

<div onclickout="console.log('clickout detected')">...</div>

Vanilla JavaScript

document.getElementById('myId').addEventListener('clickout', myListener);

jQuery

$('#myId').on('clickout', myListener);

Vue.js

<div v-on:clickout="open=false">...</div>

Angular

<div (clickout)="close()">...</div>

@Art 2010-06-12 08:35:55

You can listen for a click event on document and then make sure #menucontainer is not an ancestor or the target of the clicked element by using .closest().

If it is not, then the clicked element is outside of the #menucontainer and you can safely hide it.

$(document).click(function(event) { 
  var $target = $(event.target);
  if(!$target.closest('#menucontainer').length && 
  $('#menucontainer').is(":visible")) {
    $('#menucontainer').hide();
  }        
});

Edit – 2017-06-23

You can also clean up after the event listener if you plan to dismiss the menu and want to stop listening for events. This function will clean up only the newly created listener, preserving any other click listeners on document. With ES2015 syntax:

export function hideOnClickOutside(selector) {
  const outsideClickListener = (event) => {
    const $target = $(event.target);
    if (!$target.closest(selector).length && $(selector).is(':visible')) {
        $(selector).hide();
        removeClickListener();
    }
  }

  const removeClickListener = () => {
    document.removeEventListener('click', outsideClickListener)
  }

  document.addEventListener('click', outsideClickListener)
}

Edit – 2018-03-11

For those who don't want to use jQuery. Here's the above code in plain vanillaJS (ECMAScript6).

function hideOnClickOutside(element) {
    const outsideClickListener = event => {
        if (!element.contains(event.target) && isVisible(element)) { // or use: event.target.closest(selector) === null
          element.style.display = 'none'
          removeClickListener()
        }
    }

    const removeClickListener = () => {
        document.removeEventListener('click', outsideClickListener)
    }

    document.addEventListener('click', outsideClickListener)
}

const isVisible = elem => !!elem && !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ) // source (2018-03-11): https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js 

NOTE: This is based on Alex comment to just use !element.contains(event.target) instead of the jQuery part.

But element.closest() is now also available in all major browsers (the W3C version differs a bit from the jQuery one). Polyfills can be found here: Element.closest()

Edit – 2020-05-21

In the case where you want the user to be able to click-and-drag inside the element, then release the mouse outside the element, without closing the element:

      ...
      let lastMouseDownX = 0;
      let lastMouseDownY = 0;
      let lastMouseDownWasOutside = false;

      const mouseDownListener = (event: MouseEvent) => {
        lastMouseDownX = event.offsetX
        lastMouseDownY = event.offsetY
        lastMouseDownWasOutside = !$(event.target).closest(element).length
      }
      document.addEventListener('mousedown', mouseDownListener);

And in outsideClickListener:

const outsideClickListener = event => {
        const deltaX = event.offsetX - lastMouseDownX
        const deltaY = event.offsetY - lastMouseDownY
        const distSq = (deltaX * deltaX) + (deltaY * deltaY)
        const isDrag = distSq > 3
        const isDragException = isDrag && !lastMouseDownWasOutside

        if (!element.contains(event.target) && isVisible(element) && !isDragException) { // or use: event.target.closest(selector) === null
          element.style.display = 'none'
          removeClickListener()
          document.removeEventListener('mousedown', mouseDownListener); // Or add this line to removeClickListener()
        }
    }

@Eran Galperin 2010-06-16 20:14:47

While your method works as well, your statement is completely erroneous. #menucontainer is the bottom level in the propagation chain for all the elements it contains, therefor it doesn't change any of its behavior. You should try it and see for yourself.

@Pistos 2012-04-05 19:30:02

I tried many of the other answers, but only this one worked. Thanks. The code I ended up using was this: $(document).click( function(event) { if( $(event.target).closest('.window').length == 0 ) { $('.window').fadeOut('fast'); } } );

@umassthrower 2012-04-07 23:41:56

I actually ended up going with this solution because it better supports multiple menus on the same page where clicking on a second menu while a first is open will leave the first open in the stopPropagation solution.

@John 2013-05-08 15:23:01

Excellent answer. This is the way to go when you have multiple items which you wish to close.

@tomato 2014-04-25 02:45:33

This should be the accepted answer because of the flaw the other solution has with event.stopPropagation().

@Bill 2014-05-29 00:02:20

This should be the accepted answer, for more detailed answer refer to this page: css-tricks.com/dangers-stopping-event-propagation

@Bohdan Lyzanets 2014-07-24 09:32:01

as variant: var $menu = $('#menucontainer'); $(document).on('click', function (e) { // if element is opened and click target is outside it, // close it if ($menu.is(':visible') && !$menu.is(e.target) && !$menu.has(e.target).length) { $menu.hide(); } });

@James Heston 2014-09-14 17:05:39

This is a better solution than any that use $('html').stopPropagation(), as those are very likely to interfere the functionality of other parts of site. I would hope someone will mark this as the answer instead.

@vsync 2014-11-18 13:31:51

One line example - !$(e.target).closest('.menu').length && $('.menu').is(":visible") && $('.menu').hide();

@ncubica 2014-11-21 00:45:22

why no body has checked for tagName var $target = $(event.target); if($target.get(0).tagName.toUpperCase() === "BODY"){ console.log("you click the body"); }

@AliBZ 2015-02-23 22:56:07

closest doesn't work for dynamically loaded elements (like items loaded by backbone.marionettejs's CollectionView).

@BadHorsie 2015-03-12 13:35:51

This didn't work for me when there was only one element on the page. If I had multiple elements on the page it worked fine.

@ProblemsOfSumit 2015-06-13 13:23:21

much much better, especially with web apps that hijack internal link behaviour for HTML5 History API use. (so, all of them)

@Alex Ross 2015-07-31 01:56:15

Without jQuery - !element.contains(event.target) using Node.contains()

@pablito.aven 2015-08-20 14:37:37

This is absolutely best choice in my opinion. Stopping event propagation is very messy if the div you want to hide has something cilckable inside it.

@Wallace Maxters 2015-09-21 19:50:42

I make so: $(event.target).closest('#menucontainer').size() == 0

@Craig Jacobs 2015-11-23 21:16:02

For responsive designs rather than click use on like this: $(document).on('touchstart click', function(event){...

@ColdTuna 2016-01-07 14:07:58

This works great, however, sometimes $(event.target) comes as an empty string... any idea why? I am clicking on the same target every time.

@Bjørn Stenfeldt 2016-02-08 07:50:21

"The .closest() method begins its search with the element itself before progressing up the DOM tree" - api.jquery.com/closest. So it should be possible to remove ` && !$(event.target).is('#menucontainer')`.

@Bjørn Stenfeldt 2016-02-08 08:32:27

I had 2 document click events because 2 containers needed to close separately when clicked outside of them. This actually gave me some problems, because clicking the link to open the second container wouldn't close the first and vice versa. But separately they worked fine. The fix turned out to be the mouseup event instead of the click event on document.

@Ryan 2016-02-09 00:10:11

This seems to work but not on mobile, can this be confirmed?

@Khalid T. 2016-02-23 13:46:13

I agree with @Bjørn Stenfeldt. There is no need for the !$(event.target).is('#menucontainer') part when using .closest()

@Sprose 2016-06-06 09:41:06

Fantastic solution and works great. I just adapted the $('#menucontainer').hide(); section to $('#menucontainer').removeClass('showMenu'); to work with my all products menu.

@bdb.jack 2017-01-17 16:28:10

I'm promoting this because it has the !$(event.target).closest('#element').length check, which was exactly what i needed in my own application.

@markthewizard1234 2017-04-07 10:05:12

This is the perfect solution - it does what it needs to do without breaking other elements within the container. I had an issue where Ajax links were not working with the stopPropagation().

@Chris Chalmers 2017-07-28 23:49:53

works like a charm! I used .parents() instead of .closest() because my elements have multiple levels inside.

@Robert Munn 2019-03-19 15:00:22

If you want to limit this function to just detecting the click but not acting on it, you can wrap the entire function in a Promise, remove hiding the element, and resolve the promise after calling removeEventListener. You can use .then(...) to act on detecting the click from where you called this function. Better encapsulation, more clear in source what is happening.

@Shmalex 2019-04-01 18:03:31

thank you for your solution. It works great. But I noticed when I add this handler during another click event - the listener get called during of the event bubbling phase. To fix that problem I came up with solution like setTimeout((x)=>{ document.addEventListener('click', outsideClickListener);},1);

@Илья Зеленько 2020-06-30 12:25:12

2020 solution using native JS API closest method.

document.addEventListener('click', ({ target }) => {
  if (!target.closest('.el1, .el2, #el3')) {
    alert('click outside')
  }
})

@Steven Mark Ford 2020-09-10 01:19:53

closest is not supported by IE

@Jovanni G 2018-02-12 10:39:21

I am surprised nobody actually acknowledged focusout event:

var button = document.getElementById('button');
button.addEventListener('click', function(e){
  e.target.style.backgroundColor = 'green';
});
button.addEventListener('focusout', function(e){
  e.target.style.backgroundColor = '';
});
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
</head>
<body>
  <button id="button">Click</button>
</body>
</html>

@Muhammet Can TONBUL 2019-06-14 13:35:48

You missing my answer i think. stackoverflow.com/a/47755925/6478359

@James 2020-07-06 12:51:29

I guess no-one suggested focusout because the question is about click outside not mouse hover away. As with others, I specifically need a click as well as hover. But this'll be useful to some :)

@JBarros 2020-05-19 13:38:33

The easiest way: mouseleave(function())

More info: https://www.w3schools.com/jquery/jquery_events.asp

@Xnero 2020-05-19 14:00:55

A link to a solution is welcome, but please ensure your answer is useful without it: add context around the link so your fellow users will have some idea what it is and why it’s there, then quote the most relevant part of the page you're linking to in case the target page is unavailable. Answers that are little more than a link may be deleted.

@Paul Roub 2020-05-19 20:13:07

@Daniil this is not a link-only answer, though. If the link were removed, the first sentence would still constitute an answer.

@James 2020-07-06 10:23:28

I agree this is not a link only answer, but it's not an answer to this question as this answer is when the mouse leaves the element, and not on click as was asked ;)

@Marcelo Ribeiro 2019-12-27 18:25:13

const button = document.querySelector('button')
const box = document.querySelector('.box');

const toggle = event => {
  event.stopPropagation();
  
  if (!event.target.closest('.box')) {
    console.log('Click outside');

    box.classList.toggle('active');

    box.classList.contains('active')
      ? document.addEventListener('click', toggle)
      : document.removeEventListener('click', toggle);
  } else {
    console.log('Click inside');
  }
}

button.addEventListener('click', toggle);
.box {
  position: absolute;
  display: none;
  margin-top: 8px;
  padding: 20px;
  background: lightgray;
}

.box.active {
  display: block;
}
<button>Toggle box</button>

<div class="box">
  <form action="">
    <input type="text">
    <button type="button">Search</button>
  </form>
</div>

@Harry Moreno 2020-02-20 05:56:32

For those wondering how this works, the callback toggle is relying on the browser's default event propagation. If the click is inside the "box" do not hide the box. Else, toggle the active class. See we need to enter the condition if the user clicks outside the box OR on the button. If we toggled active ON register the same callback on the document root, ELSE remove the callback from the root.

@Dan Philip 2017-04-14 04:27:58

The event has a property called event.path of the element which is a "static ordered list of all its ancestors in tree order". To check if an event originated from a specific DOM element or one of its children, just check the path for that specific DOM element. It can also be used to check multiple elements by logically ORing the element check in the some function.

$("body").click(function() {
  target = document.getElementById("main");
  flag = event.path.some(function(el, i, arr) {
    return (el == target)
  })
  if (flag) {
    console.log("Inside")
  } else {
    console.log("Outside")
  }
});
#main {
  display: inline-block;
  background:yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="main">
  <ul>
    <li>Test-Main</li>
    <li>Test-Main</li>
    <li>Test-Main</li>
    <li>Test-Main</li>
    <li>Test-Main</li>
  </ul>
</div>
<div id="main2">
  Outside Main
</div>

So for your case It should be

$("body").click(function() {
  target = $("#menuscontainer")[0];
  flag = event.path.some(function(el, i, arr) {
    return (el == target)
  });
  if (!flag) {
    // Hide the menus
  }
});

@Andrew 2020-05-25 19:09:44

event.path is not a thing.

@Rivenfall 2019-06-25 15:23:25

First you have to track wether the mouse is inside or outside your element1, using the mouseenter and mouseleave events. Then you can create an element2 which covers the whole screen to detect any clicks, and react accordingly depending on wether you are inside or outside element1.

I strongly recommend to handle both initialization and cleanup, and that the element2 is made as temporary as possible, for obvious reasons.

In the example below, the overlay is an element positionned somewhere, which can be selected by clicking inside, and unselected by clicking outside. The _init and _release methods are called as part of an automatic initialisation/cleanup process. The class inherits from a ClickOverlay which has an inner and outerElement, don't worry about it. I used outerElement.parentNode.appendChild to avoid conflicts.

import ClickOverlay from './ClickOverlay.js'

/* CSS */
// .unselect-helper {
//  position: fixed; left: -100vw; top: -100vh;
//  width: 200vw; height: 200vh;
// }
// .selected {outline: 1px solid black}

export default class ResizeOverlay extends ClickOverlay {
    _init(_opts) {
        this.enterListener = () => this.onEnter()
        this.innerElement.addEventListener('mouseenter', this.enterListener)
        this.leaveListener = () => this.onLeave()
        this.innerElement.addEventListener('mouseleave', this.leaveListener)
        this.selectListener = () => {
            if (this.unselectHelper)
                return
            this.unselectHelper = document.createElement('div')
            this.unselectHelper.classList.add('unselect-helper')
            this.unselectListener = () => {
                if (this.mouseInside)
                    return
                this.clearUnselectHelper()
                this.onUnselect()
            }
            this.unselectHelper.addEventListener('pointerdown'
                , this.unselectListener)
            this.outerElement.parentNode.appendChild(this.unselectHelper)
            this.onSelect()
        }
        this.innerElement.addEventListener('pointerup', this.selectListener)
    }

    _release() {
        this.innerElement.removeEventListener('mouseenter', this.enterListener)
        this.innerElement.removeEventListener('mouseleave', this.leaveListener)
        this.innerElement.removeEventListener('pointerup', this.selectListener)
        this.clearUnselectHelper()
    }

    clearUnselectHelper() {
        if (!this.unselectHelper)
            return
        this.unselectHelper.removeEventListener('pointerdown'
            , this.unselectListener)
        this.unselectHelper.remove()
        delete this.unselectListener
        delete this.unselectHelper
    }

    onEnter() {
        this.mouseInside = true
    }

    onLeave() {
        delete this.mouseInside
    }

    onSelect() {
        this.innerElement.classList.add('selected')
    }

    onUnselect() {
        this.innerElement.classList.remove('selected')
    }
}

@Alejandro Vales 2019-07-25 11:16:02

This is way too much code for the answer that was already given... Why would you make this? Wasting a lot of event listeners, just for what would be adding 2 onclicks, one on the element, another on the body closing the element that you want closed

@Yair Cohen 2019-02-11 14:47:00

Let's say the div you want to detect if the user clicked outside or inside has an id, for example: "my-special-widget".

Listen to body click events:

document.body.addEventListener('click', (e) => {
    if (isInsideMySpecialWidget(e.target, "my-special-widget")) {
        console.log("user clicked INSIDE the widget");
    }
    console.log("user clicked OUTSIDE the widget");
});

function isInsideMySpecialWidget(elem, mySpecialWidgetId){
    while (elem.parentElement) {
        if (elem.id === mySpecialWidgetId) {
            return true;
        }
        elem = elem.parentElement;
    }
    return false;
}

In this case, you won't break the normal flow of click on some element in your page, since you are not using the "stopPropagation" method.

@Eran Galperin 2008-09-30 13:38:11

NOTE: Using stopEventPropagation() is something that should be avoided as it breaks normal event flow in the DOM. See this article for more information. Consider using this method instead

Attach a click event to the document body which closes the window. Attach a separate click event to the container which stops propagation to the document body.

$(window).click(function() {
//Hide the menus if visible
});

$('#menucontainer').click(function(event){
    event.stopPropagation();
});

@vsync 2009-08-17 16:47:39

I prefer to bind the document to the click event, then unbind the event when needed. its more efficient.

@Art 2010-06-12 08:00:10

This breaks standard behaviour of many things, including buttons and links, contained within #menucontainer. I am surprised this answer is so popular.

@Art 2010-06-12 08:38:16

I have posted an alternative solution, which does not break he behaviour stackoverflow.com/questions/152975/…

@Eran Galperin 2010-06-16 19:55:20

This doesn't break behavior of anything inside #menucontainer, since it is at the bottom of the propagation chain for anything inside of it.

@meo 2011-02-25 15:35:29

its very beautyfull but you should use $('html').click() not body. The body always has the height of its content. It there is not a lot of content or the screen is very high, it only works on the part filled by the body.

@Frederik Wordenskjold 2011-03-19 02:43:24

meo + 1. Should be html instead of body. Same argument for horizontal space; if a page has fluid margins, and the monitor is wider than the content, the menu will not hide when clicking outside of the area if you use body.

@Mathias Bynens 2011-09-27 12:18:39

@medo It should really be $(document).

@Prusprus 2011-11-18 15:59:19

@Art a simple workaround for this would be to stop the propagation the wrapper div that contains the content for the menu.

@tundoopani 2011-12-31 06:00:18

@user751564 how would we do that? Nest another div with the content inside #menucontainer?

@umassthrower 2012-04-07 23:22:33

Evan, Sergio, Joe, Art, meo, and everyone else (+Joel & Jeff) I love you all. It would have taken me a day to eventually get around to realizing event.stopPropigation in this case.

@umassthrower 2012-04-07 23:44:57

Err, actually I went with Art's solution because I didn't want to stop propagation when someone clicks a second menu while the first was still open.

@Andre 2012-05-08 12:32:44

I am also surprised that this solution got so many votes. This will fail for any element outside that has stopPropagation jsfiddle.net/Flandre/vaNFw/3

@Owen 2012-10-16 17:45:10

stackoverflow.com/a/12920446/470159 I've answered a similar one with what i feel is a nice solution

@Diogo Kollross 2013-01-18 23:49:59

If you show the menu from a click event (eg: a button or link), then the "open menu" click event handler should also call event.stopPropagation().

@Anders 2013-06-18 13:35:46

Nice solution, small and light. But... Does not take overlays like datepickers, popups etc into account. Elements likes these should not trigger hide in my opinion. I added a answer to this question that solves this.

@Stephen Corwin 2013-10-27 02:09:19

Doesn't work if you have more than one menu on the page using this technique. Clicking on any one menu will stop all the other menus from closing.

@Andre Figueiredo 2013-11-29 13:13:06

I cannot say I am surprised this answer got so many upvotes, it works in many cases. But this is not the best solution for this problem! Personally, I like to hand things inside a function in the function scope: stackoverflow.com/questions/152975/… stackoverflow.com/a/7385673/986862

@Adrien Be 2014-04-16 09:32:45

stackoverflow.com/questions/1403615/… complete this answer as pointed by @AndréFigueiredo:

@Tom 2014-05-20 10:52:54

Philip Walton explains very well why this answer isn't the best solution: css-tricks.com/dangers-stopping-event-propagation

@Andy Mercer 2014-05-23 19:07:21

I'm downvoting this because it's not really a solution we should be using. It's dangerous because it has far reaching consequences. As Tom pointed out in a comment above, there is a very in depth article on css-tricks.org about why this answer good.

@Ian S 2014-07-11 19:54:36

This solution breaks other sections of my code. Sorry but i'll have to downvote this. I'm also surprised this is so popular.

@AlexG 2015-02-25 12:09:38

That solution is generally terrible and a maintenance hazard. Basically just a hack that sometimes happen to work.

@Christian Rolle 2015-04-21 14:13:59

This answer is breaking a lot standard behaviour. It should downrated!

@Belal mazlom 2015-08-10 09:25:40

I think this dangers on page performance, details inthis article: css-tricks.com/dangers-stopping-event-propagation

@codeepic 2016-07-25 09:01:14

Here is actually a good explanation why not to use event.stopPropagation() css-tricks.com/dangers-stopping-event-propagation

@Dan Philip 2017-04-14 11:20:24

@Martin James 2018-07-04 15:54:29

I regretfully used a modal plugin that was shockingly using this method to allow clicking outside of the modal to close it, and it took forever to find out why I couldn't delegate events to anything inside of the modal!! I changed the plugin's code to use this instead: $(document).on('click', function(e) { if (!$(e.target).closest('.modal').length && !$(e.target).is('.modal')) modal.close(); }); which is a better solution.

@grant sun 2018-11-20 17:46:17

I would say,:don't use click event, instead, use mouseup event which is less used by other logic

@Sharpey 2018-12-12 00:24:16

Only this solution worked for me. None of those "better" below did. Thank you a lot

@Chu Yeow 2010-12-02 09:53:12

I've had success with something like this:

var $menuscontainer = ...;

$('#trigger').click(function() {
  $menuscontainer.show();

  $('body').click(function(event) {
    var $target = $(event.target);

    if ($target.parents('#menuscontainer').length == 0) {
      $menuscontainer.hide();
    }
  });
});

The logic is: when #menuscontainer is shown, bind a click handler to the body that hides #menuscontainer only if the target (of the click) isn't a child of it.

@DaniG2k 2018-03-07 08:49:30

I just want to make @Pistos answer more apparent since it's hidden in the comments.

This solution worked perfectly for me. Plain JS:

var elementToToggle = $('.some-element');
$(document).click( function(event) {
  if( $(event.target).closest(elementToToggle).length === 0 ) {
    elementToToggle.hide();
  }
});

in CoffeeScript:

elementToToggle = $('.some-element')
$(document).click (event) ->
  if $(event.target).closest(elementToToggle).length == 0
    elementToToggle.hide()

@Rinto George 2018-01-14 08:29:37

I have used below script and done with jQuery.

jQuery(document).click(function(e) {
    var target = e.target; //target div recorded
    if (!jQuery(target).is('#tobehide') ) {
        jQuery(this).fadeOut(); //if the click element is not the above id will hide
    }
})

Below find the HTML code

<div class="main-container">
<div> Hello I am the title</div>
<div class="tobehide">I will hide when you click outside of me</div>
</div>

You can read the tutorial here

@chea sotheara 2018-01-09 03:42:47

$('#propertyType').on("click",function(e){
          self.propertyTypeDialog = !self.propertyTypeDialog;
          b = true;
          e.stopPropagation();
          console.log("input clicked");
      });

      $(document).on('click','body:not(#propertyType)',function (e) {
          e.stopPropagation();
          if(b == true)  {
              if ($(e.target).closest("#configuration").length == 0) {
                  b = false;
                  self.propertyTypeDialog = false;
                  console.log("outside clicked");
              }
          }
        // console.log($(e.target).closest("#configuration").length);
      });

@Muhammet Can TONBUL 2017-12-11 15:15:33

If you are using tools like "Pop-up", you can use the "onFocusOut" event.

window.onload=function(){
document.getElementById("inside-div").focus();
}
function loseFocus(){
alert("Clicked outside");
}
#container{
background-color:lightblue;
width:200px;
height:200px;
}

#inside-div{
background-color:lightgray;
width:100px;
height:100px;

}
<div id="container">
<input type="text" id="inside-div" onfocusout="loseFocus()">
</div>

@Aominé 2017-12-09 13:42:54

if you just want to display a window when you click on a button and undisp this window when you click outside.( or on the button again ) this bellow work good

document.body.onclick = function() { undisp_menu(); };
var menu_on = 0;

function menu_trigger(event){

    if (menu_on == 0)
    {
        // otherwise u will call the undisp on body when 
        // click on the button
        event.stopPropagation(); 

        disp_menu();
    }

    else{
        undisp_menu();
    }

}


function disp_menu(){

    menu_on = 1;
    var e = document.getElementsByClassName("menu")[0];
    e.className = "menu on";

}

function undisp_menu(){

    menu_on = 0;
    var e = document.getElementsByClassName("menu")[0];
    e.className = "menu";

}

don't forget this for the button

<div class="button" onclick="menu_trigger(event)">

<div class="menu">

and the css:

.menu{
    display: none;
}

.on {
    display: inline-block;
}

@Duannx 2017-11-03 07:50:37

Here is a simple solution by pure javascript. It is up-to-date with ES6:

var isMenuClick = false;
var menu = document.getElementById('menuscontainer');
document.addEventListener('click',()=>{
    if(!isMenuClick){
       //Hide the menu here
    }
    //Reset isMenuClick 
    isMenuClick = false;
})
menu.addEventListener('click',()=>{
    isMenuClick = true;
})

@MortenMoulder 2017-11-17 12:41:40

"Up-to-date with ES6" is a pretty bold claim, when the only thing up-to-date with ES6 is doing () => {} instead of function() {}. What you have there is classified as plain JavaScript with a twist of ES6.

@Duannx 2017-11-18 02:40:01

@MortenMoulder: Ya. It's just for attention even though it is actually ES6. But just look at the solution. I think it is good.

@Alice 2020-02-18 14:29:11

It's vanilla JS and works for event target removed from DOM (e.g. when value from inner popup is selected, immediately closing the popup). +1 from me!

@hienbt88 2017-09-13 02:11:50

This works for me

$("body").mouseup(function(e) {
    var subject = $(".main-menu");
    if(e.target.id != subject.attr('id') && !subject.has(e.target).length) {
        $('.sub-menu').hide();
    }
});

@Fabian 2017-07-27 14:00:08

Here is what I do to solve to problem.

$(window).click(function (event) {
    //To improve performance add a checklike 
    //if(myElement.isClosed) return;
    var isClickedElementChildOfMyBox = isChildOfElement(event,'#id-of-my-element');

    if (isClickedElementChildOfMyBox)
        return;

    //your code to hide the element 
});

var isChildOfElement = function (event, selector) {
    if (event.originalEvent.path) {
        return event.originalEvent.path[0].closest(selector) !== null;
    }

    return event.originalEvent.originalTarget.closest(selector) !== null;
}

@Walt 2017-07-18 00:43:37

If someone curious here is javascript solution(es6):

window.addEventListener('mouseup', e => {
        if (e.target != yourDiv && e.target.parentNode != yourDiv) {
            yourDiv.classList.remove('show-menu');
            //or yourDiv.style.display = 'none';
        }
    })

and es5, just in case:

window.addEventListener('mouseup', function (e) {
if (e.target != yourDiv && e.target.parentNode != yourDiv) {
    yourDiv.classList.remove('show-menu'); 
    //or yourDiv.style.display = 'none';
}

});

@Jitendra Damor 2015-12-15 03:50:58

A simple solution for the situation is:

$(document).mouseup(function (e)
{
    var container = $("YOUR SELECTOR"); // Give you class or ID

    if (!container.is(e.target) &&            // If the target of the click is not the desired div or section
        container.has(e.target).length === 0) // ... nor a descendant-child of the container
    {
        container.hide();
    }
});

The above script will hide the div if outside of the div click event is triggered.

You can see the following blog for more information : http://www.codecanal.com/detect-click-outside-div-using-javascript/

@Wolfram 2010-04-05 10:07:36

Now there is a plugin for that: outside events (blog post)

The following happens when a clickoutside handler (WLOG) is bound to an element:

  • the element is added to an array which holds all elements with clickoutside handlers
  • a (namespaced) click handler is bound to the document (if not already there)
  • on any click in the document, the clickoutside event is triggered for those elements in that array that are not equal to or a parent of the click-events target
  • additionally, the event.target for the clickoutside event is set to the element the user clicked on (so you even know what the user clicked, not just that he clicked outside)

So no events are stopped from propagation and additional click handlers may be used "above" the element with the outside-handler.

@TechNyquist 2017-04-11 06:44:32

Nice plugin. Link on "outside events" is dead, while blog post link is alive instead and provides a very useful plugin for "clickoutside" kind events. It's also MIT licensed.

@Gavin 2018-03-21 13:48:35

Great plugin. Worked perfectly. Usage is like so: $( '#element' ).on( 'clickoutside', function( e ) { .. } );

@karthikeyan ganesan 2017-02-24 21:32:00

$(document).on("click",function (event)   
 {   
     console.log(event);
   if ($(event.target).closest('.element').length == 0)
     {
    //your code here
      if ($(".element").hasClass("active"))
      {
        $(".element").removeClass("active");
      }
     }
 });

Try this coding for getting the solution.

@webenformasyon 2011-01-09 23:47:54

Use:

var go = false;
$(document).click(function(){
    if(go){
        $('#divID').hide();
        go = false;
    }
})

$("#divID").mouseover(function(){
    go = false;
});

$("#divID").mouseout(function (){
    go = true;
});

$("btnID").click( function(){
    if($("#divID:visible").length==1)
        $("#divID").hide(); // Toggle
    $("#divID").show();
});

@Lucas 2017-02-09 00:43:51

I know there are a million answers to this question, but I've always been a fan of using HTML and CSS to do most of the work. In this case, z-index and positioning. The simplest way that I have found to do this is as follows:

$("#show-trigger").click(function(){
  $("#element").animate({width: 'toggle'});
  $("#outside-element").show();
});
$("#outside-element").click(function(){
  $("#element").hide();
  $("#outside-element").hide();
});
#outside-element {
  position:fixed;
  width:100%;
  height:100%;
  z-index:1;
  display:none;
}
#element {
  display:none;
  padding:20px;
  background-color:#ccc;
  width:300px;
  z-index:2;
  position:relative;
}
#show-trigger {
  padding:20px;
  background-color:#ccc;
  margin:20px auto;
  z-index:2;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="outside-element"></div>
<div id="element">
  <div class="menu-item"><a href="#1">Menu Item 1</a></div>
  <div class="menu-item"><a href="#2">Menu Item 1</a></div>
  <div class="menu-item"><a href="#3">Menu Item 1</a></div>
  <div class="menu-item"><a href="#4">Menu Item 1</a></div>
</div>
<div id="show-trigger">Show Menu</div>

This creates a safe environment, since nothing is going to get triggered unless the menu is actually open and the z-index protects any of the content within the element from creating any misfires upon being clicked.

Additionally, you're not requiring jQuery to cover all of your bases with propagation calls and having to purge all of the inner elements from misfires.

@Waheed 2016-11-01 09:55:50

This might be a better fix for some people.

$(".menu_link").click(function(){
    // show menu code
});

$(".menu_link").mouseleave(function(){
    //hide menu code, you may add a timer for 3 seconds before code to be run
});

I know mouseleave does not only mean a click outside, it also means leaving that element's area.

Once the menu itself is inside the menu_link element then the menu itself should not be a problem to click on or move on.

@Puerto AGP 2017-11-30 23:40:55

mouseleave and some sort of hack might solve it for some people, here's a test jsfiddle.net/1r73jm8m

@Waltur Buerk 2016-11-12 13:57:44

I believe the best way of doing it is something like this.

$(document).on("click", function(event) {
  clickedtarget = $(event.target).closest('#menuscontainer');
  $("#menuscontainer").not(clickedtarget).hide();
});

This type of solution could easily be made to work for multiple menus and also menus that are dynamically added through javascript. Basically it just allows you to click anywhere in your document, and checks which element you clicked in, and selects it's closest "#menuscontainer". Then it hides all menuscontainers but excludes the one you clicked in.

Not sure about exactly how your menus are built, but feel free to copy my code in the JSFiddle. It's a very simple but thoroughly functional menu/modal system. All you need to do is build the html-menus and the code will do the work for you.

https://jsfiddle.net/zs6anrn7/

@Rameez Rami 2015-11-02 08:33:34

After research I have found three working solutions (I forgot the page links for reference)

First solution

<script>
    //The good thing about this solution is it doesn't stop event propagation.

    var clickFlag = 0;
    $('body').on('click', function () {
        if(clickFlag == 0) {
            console.log('hide element here');
            /* Hide element here */
        }
        else {
            clickFlag=0;
        }
    });
    $('body').on('click','#testDiv', function (event) {
        clickFlag = 1;
        console.log('showed the element');
        /* Show the element */
    });
</script>

Second solution

<script>
    $('body').on('click', function(e) {
        if($(e.target).closest('#testDiv').length == 0) {
           /* Hide dropdown here */
        }
    });
</script>

Third solution

<script>
    var specifiedElement = document.getElementById('testDiv');
    document.addEventListener('click', function(event) {
        var isClickInside = specifiedElement.contains(event.target);
        if (isClickInside) {
          console.log('You clicked inside')
        }
        else {
          console.log('You clicked outside')
        }
    });
</script>

@dbarth 2016-07-27 14:49:28

The third solution is by far the most elegant way of checking. It also doesn't involve any overhead of jQuery. Very nice. It helped a lot. Thanks.

@lowtechsun 2017-01-16 08:42:00

I am trying to get the third solution going with multiple elements document.getElementsByClassName, if someone has a clue please share.

@Donnie D'Amato 2017-02-13 17:28:10

@lowtechsun You'd have to loop through to check each.

@indiehjaerta 2018-04-14 00:49:39

Really liked the third solution, however the click triggers before my div has begun to show which hides it again, any idea why?

@RolandiXor 2018-10-09 11:32:44

The third one allows for console.log, sure, but it doesn't let you actually close by setting display to none - because it will do this before the menu is shown. Do you have a solution for this?

@Saabbir 2019-09-25 13:22:57

DOMTokenList.contains() method is the solution.

@user4639281 2015-05-19 22:52:49

Here is the vanilla JavaScript solution for future viewers.

Upon clicking any element within the document, if the clicked element's id is toggled, or the hidden element is not hidden and the hidden element does not contain the clicked element, toggle the element.

(function () {
    "use strict";
    var hidden = document.getElementById('hidden');
    document.addEventListener('click', function (e) {
        if (e.target.id == 'toggle' || (hidden.style.display != 'none' && !hidden.contains(e.target))) hidden.style.display = hidden.style.display == 'none' ? 'block' : 'none';
    }, false);
})();

(function () {
    "use strict";
    var hidden = document.getElementById('hidden');
    document.addEventListener('click', function (e) {
        if (e.target.id == 'toggle' || (hidden.style.display != 'none' && !hidden.contains(e.target))) hidden.style.display = hidden.style.display == 'none' ? 'block' : 'none';
    }, false);
})();
<a href="javascript:void(0)" id="toggle">Toggle Hidden Div</a>
<div id="hidden" style="display: none;">This content is normally hidden. click anywhere other than this content to make me disappear</div>

If you are going to have multiple toggles on the same page you can use something like this:

  1. Add the class name hidden to the collapsible item.
  2. Upon document click, close all hidden elements which do not contain the clicked element and are not hidden
  3. If the clicked element is a toggle, toggle the specified element.

(function () {
    "use strict";
    var hiddenItems = document.getElementsByClassName('hidden'), hidden;
    document.addEventListener('click', function (e) {
        for (var i = 0; hidden = hiddenItems[i]; i++) {
            if (!hidden.contains(e.target) && hidden.style.display != 'none')
                hidden.style.display = 'none';
        }
        if (e.target.getAttribute('data-toggle')) {
            var toggle = document.querySelector(e.target.getAttribute('data-toggle'));
            toggle.style.display = toggle.style.display == 'none' ? 'block' : 'none';
        }
    }, false);
})();
<a href="javascript:void(0)" data-toggle="#hidden1">Toggle Hidden Div</a>
<div class="hidden" id="hidden1" style="display: none;" data-hidden="true">This content is normally hidden</div>
<a href="javascript:void(0)" data-toggle="#hidden2">Toggle Hidden Div</a>
<div class="hidden" id="hidden2" style="display: none;" data-hidden="true">This content is normally hidden</div>
<a href="javascript:void(0)" data-toggle="#hidden3">Toggle Hidden Div</a>
<div class="hidden" id="hidden3" style="display: none;" data-hidden="true">This content is normally hidden</div>

Related Questions

Sponsored Content

98 Answered Questions

[SOLVED] How can I remove a specific item from an array?

  • 2011-04-23 22:17:18
  • Walker
  • 7147809 View
  • 8662 Score
  • 98 Answer
  • Tags:   javascript arrays

58 Answered Questions

[SOLVED] How do I redirect to another webpage?

19 Answered Questions

[SOLVED] Where should I put <script> tags in HTML markup?

59 Answered Questions

[SOLVED] How do I check if an element is hidden in jQuery?

86 Answered Questions

[SOLVED] How do JavaScript closures work?

39 Answered Questions

[SOLVED] How do I return the response from an asynchronous call?

32 Answered Questions

[SOLVED] jQuery scroll to element

59 Answered Questions

[SOLVED] What is the best way to detect a mobile device?

14 Answered Questions

[SOLVED] How can I select an element by name with jQuery?

3 Answered Questions

Sponsored Content