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
});
31 upvote
  flag
Here's a sample of this strategy: jsfiddle.net/tedp/aL7Xe/1 – Ted
12 upvote
  flag
As Tom mentioned, you'll want to read css-tricks.com/dangers-stopping-event-propagation before using this approach. That jsfiddle tool is pretty cool though. – Jon Coombs
1 upvote
  flag
get a reference to the element and then event.target, and finally != or == both of them then execute code accordingly.. – Rohit Kumar
upvote
  flag
upvote
  flag
upvote
  flag
Vanilla JS solution with event.target and without event.stopPropagation. – lowtechsun

74 Answers 11

Check the window click event target (it should propagate to the window, as long as it's not captured anywhere else), and ensure that it's not any of the menu elements. If it's not, then you're outside your menu.

Or check the position of the click, and see if it's contained within the menu area.

up vote 1558 down vote accepted

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 window which stops propagation to the document body.

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

$('#menucontainer').click(function(event){
    event.stopPropagation();
});
25 upvote
  flag
I prefer to bind the document to the click event, then unbind the event when needed. its more efficient. – vsync
564 upvote
  flag
This breaks standard behaviour of many things, including buttons and links, contained within #menucontainer. I am surprised this answer is so popular. – Art
5 upvote
  flag
I have posted an alternative solution, which does not break he behaviour //allinonescript.com/questions/152975/… – Art
62 upvote
  flag
This doesn't break behavior of anything inside #menucontainer, since it is at the bottom of the propagation chain for anything inside of it. – Eran Galperin
82 upvote
  flag
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. – meo
1 upvote
  flag
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. – Frederik Wordenskjold
4 upvote
  flag
@medo It should really be $(document). – Mathias Bynens
1 upvote
  flag
@Art a simple workaround for this would be to stop the propagation the wrapper div that contains the content for the menu. – Prusprus
upvote
  flag
@user751564 how would we do that? Nest another div with the content inside #menucontainer? – tundoopani
upvote
  flag
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
6 upvote
  flag
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. – umassthrower
79 upvote
  flag
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 – Andre
3 upvote
  flag
//allinonescript.com/a/12920446/470159 I've answered a similar one with what i feel is a nice solution – Owen
upvote
  flag
and how to show it from a link in the html? – Martin
upvote
  flag
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(). – Diogo Kollross
upvote
  flag
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. – Anders
4 upvote
  flag
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. – Stephen Corwin
3 upvote
  flag
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: //allinonescript.com/questions/152975/… //allinonescript.com/a/7385673/986862 – Andre Figueiredo
1 upvote
  flag
//allinonescript.com/questions/1403615/… complete this answer as pointed by @AndréFigueiredo: – Adrien Be
113 upvote
  flag
Philip Walton explains very well why this answer isn't the best solution: css-tricks.com/dangers-stopping-event-propagation – Tom
9 upvote
  flag
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. – Andy Mercer
4 upvote
  flag
This solution breaks other sections of my code. Sorry but i'll have to downvote this. I'm also surprised this is so popular. – Ian Steffy
8 upvote
  flag
This shouldn't be the accepted answer. – Derek 朕會功夫
6 upvote
  flag
That solution is generally terrible and a maintenance hazard. Basically just a hack that sometimes happen to work. – AlexG
8 upvote
  flag
This answer is breaking a lot standard behaviour. It should downrated! – Christian Rolle
1 upvote
  flag
I think this dangers on page performance, details inthis article: css-tricks.com/dangers-stopping-event-propagation – Belal mazlom
2 upvote
  flag
This clearly isn't the best practice. – Damjan Pavlica
2 upvote
  flag
Here is actually a good explanation why not to use event.stopPropagation() css-tricks.com/dangers-stopping-event-propagation – codeepic
upvote
  flag
1 upvote
  flag
I've added a warning to the answer so that people stop blindly copying and pasting this answer. – this.lau_
upvote
  flag
Awesome solution. Just when I thought it will not be possible with jQuery. – Jasmeet Singh

I have an application that works similarly to Eran's example, except I attach the click event to the body when I open the menu... Kinda like this:

$('#menucontainer').click(function(event) {
  $('html').one('click',function() {
    // Hide the menus
  });

  event.stopPropagation();
});

More information on jQuery's one() function

7 upvote
  flag
but then if you click on the menu itself, then outside, it won't work :) – vsync
2 upvote
  flag
It helps to put event.stopProgagantion() before binding the click listener to body. – Jasper Kennis
3 upvote
  flag
The problem with this is that "one" applies to the jQuery method of adding events to an array multiple times. So if you click on the menu to open it more than once, the event is bound to the body again and attempts to hide the menu multiple times. A failsafe should be applied to fix this issue. – marksyzm
upvote
  flag
After binding using .one -- inside of the $('html') handler -- write a $('html').off('click'). – Cody
3 upvote
  flag
@Cody I don't think that helps. The one handler will automatically call off (as it's shown in the jQuery docs). – Mariano Desanze
upvote
  flag
Perhaps I misread the implementation. My own implementation of this requires that you have 2 actions -- each correlating to a toggling of the event-listener. One action sets it, one removes it. This simply depends on where in your code you are actually setting the listener -- otherwise you can still have multiple handlers invoked based upon the number of times the setting-action is called. – Cody

The other solutions here didn't work for me so I had to use:

if(!$(event.target).is('#foo'))
{
    // hide menu
}
upvote
  flag
I've posted another practical example of how to use event.target to avoid triggering other Jquery UI widget outside-click html handlers when embedding them in your own pop-over box: Best way to get the Original Target – Joey T
36 upvote
  flag
This worked for me, except I added && !$(event.target).parents("#foo").is("#foo") inside the IF statement so that any child elements won't close the menu when clicked. – honyovk
upvote
  flag
@Frug Your reply may be valuable so you got 5, but I got confused by your reply. and who is MBJ. I was looking to create better solution based on your reply. – Satya Prakash
upvote
  flag
MBJ was probably the old name of the person who posted the comment just above mine. Users change names, it can make it difficult but notice the comment above mine has an added .parents("#foo") – Frug
upvote
  flag
Can't find better than: // We're outside $(event.target).parents('#foo').length == 0 – AlexG
upvote
  flag
Best in case of hiding dropdowns on click of their outside – Farzad YZ
1 upvote
  flag
The concise improvement to handle deep nesting is to use .is('#foo, #foo *'), but I don't recommend binding click handlers to solve this issue. – zzzzBov
upvote
  flag
👍👍 to @zzzzBov's comment. – Chris Calo

If you are scripting for IE and FF 3.* and you just want to know if the click occured within a certain box area, you could also use something like:

this.outsideElementClick = function(objEvent, objElement){   
var objCurrentElement = objEvent.target || objEvent.srcElement;
var blnInsideX = false;
var blnInsideY = false;

if (objCurrentElement.getBoundingClientRect().left >= objElement.getBoundingClientRect().left && objCurrentElement.getBoundingClientRect().right <= objElement.getBoundingClientRect().right)
    blnInsideX = true;

if (objCurrentElement.getBoundingClientRect().top >= objElement.getBoundingClientRect().top && objCurrentElement.getBoundingClientRect().bottom <= objElement.getBoundingClientRect().bottom)
    blnInsideY = true;

if (blnInsideX && blnInsideY)
    return false;
else
    return true;}
$("#menuscontainer").click(function() {
    $(this).focus();
});
$("#menuscontainer").blur(function(){
    $(this).hide();
});

Works for me just fine.

1 upvote
  flag
This is the one I'd use. It may not be perfect, but as a hobby programmer, its simple enough to understand clearly. – kevtrout
upvote
  flag
blur is a move outside the #menucontainer the question was about a click – borrel
3 upvote
  flag
@borrel blur is not a move outside the container. Blur is the opposite of focus, you are thinking of mouseout. This solution worked particularly well for me when I was creating "click to edit" text where I swapped back and forth between plain text and input fields on clicks. – parker.sikand
upvote
  flag
I had to add tabindex="-1" to the #menuscontainer to make it work. It seems that if you put an input tag inside the container and click on it, the container gets hidden. – tyrion
upvote
  flag
the mouseleave event is more suitable for menus and containers (ref: w3schools.com/jquery/…) – Evgenia Manolova

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.

upvote
  flag
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. – TechNyquist

This worked perfectly fine in time for me:

$('body').click(function() {
    // Hide the menus if visible.
});
2 upvote
  flag
the problem whit this its that no matter where you click, you always gonna have the event bubble down to the body. so you should use event.stopPropagation but that would break that natural way of how the DOM Behave I dont think this is a good practice – ncubica

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) { 
    if(!$(event.target).closest('#menucontainer').length) {
        if($('#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) => {
    if (!$(event.target).closest(selector).length) {
      if ($(selector).is(':visible')) {
        $(selector).hide()
        removeClickListener()
      }
    }
  }

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

  document.addEventListener('click', outsideClickListener)
}
1 upvote
  flag
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. – Eran Galperin
8 upvote
  flag
Had issues with the live() and this works like charm! cheers! – chchrist
17 upvote
  flag
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'); } } ); – Pistos
28 upvote
  flag
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. – umassthrower
9 upvote
  flag
This is a very good solution for multiple items on the page. – jsgroove
8 upvote
  flag
Excellent answer. This is the way to go when you have multiple items which you wish to close. – John
3 upvote
  flag
This should be the accepted answer because of the flaw the other solution has with event.stopPropagation(). – developer.ejay
4 upvote
  flag
This should be the accepted answer, for more detailed answer refer to this page: css-tricks.com/dangers-stopping-event-propagation – Bruce
upvote
  flag
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(); } }); – Bohdan Lyzanets
3 upvote
  flag
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. – James Heston
upvote
  flag
One line example - !$(e.target).closest('.menu').length && $('.menu').is(":visible") && $('.menu').hide(); – vsync
upvote
  flag
why no body has checked for tagName var $target = $(event.target); if($target.get(0).tagName.toUpperCase() === "BODY"){ console.log("you click the body"); } – ncubica
upvote
  flag
closest doesn't work for dynamically loaded elements (like items loaded by backbone.marionettejs's CollectionView). – AliBZ
upvote
  flag
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. – BadHorsie
1 upvote
  flag
much much better, especially with web apps that hijack internal link behaviour for HTML5 History API use. (so, all of them) – Sumit
6 upvote
  flag
Without jQuery - !element.contains(event.target) using Node.contains() – Alex Ross
upvote
  flag
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. – pablito.aven
upvote
  flag
I make so: $(event.target).closest('#menucontainer').size() == 0 – Wallace de Souza
2 upvote
  flag
For responsive designs rather than click use on like this: $(document).on('touchstart click', function(event){... – Craig Jacobs
upvote
  flag
This works great, however, sometimes $(event.target) comes as an empty string... any idea why? I am clicking on the same target every time. – Caio Caio
4 upvote
  flag
"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
1 upvote
  flag
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. – Bjørn Stenfeldt
upvote
  flag
This seems to work but not on mobile, can this be confirmed? – Ryan Little
1 upvote
  flag
I agree with @Bjørn Stenfeldt. There is no need for the !$(event.target).is('#menucontainer') part when using .closest() – Khalid T.
1 upvote
  flag
Fantastic solution and works great. I just adapted the $('#menucontainer').hide(); section to $('#menucontainer').removeClass('showMenu'); to work with my all products menu. – Sprose
1 upvote
  flag
Brilliant answer! Work exactly as needed! – volodymyr3131
1 upvote
  flag
I'm promoting this because it has the !$(event.target).closest('#element').length check, which was exactly what i needed in my own application. – bdb.jack
1 upvote
  flag
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(). – markthewizard1234
upvote
  flag
I think const outsideClickListener = (e) => { should have event as the parameter. – Asim K T
1 upvote
  flag
works like a charm! I used .parents() instead of .closest() because my elements have multiple levels inside. – Chris Chalmers
1 upvote
  flag
Should be marked as BEST ANSWER – Christian Michael
upvote
  flag
Thanks for the non-jQuery answer, Alex! – MarsAndBack

Function:

$(function() {
    $.fn.click_inout = function(clickin_handler, clickout_handler) {
        var item = this;
        var is_me = false;
        item.click(function(event) {
            clickin_handler(event);
            is_me = true;
        });
        $(document).click(function(event) {
            if (is_me) {
                is_me = false;
            } else {
                clickout_handler(event);
            }
        });
        return this;
    }
});

Usage:

this.input = $('<input>')
    .click_inout(
        function(event) { me.ShowTree(event); },
        function() { me.Hide(); }
    )
    .appendTo(this.node);

And functions are very simple:

ShowTree: function(event) {
    this.data_span.show();
}
Hide: function() {
    this.data_span.hide();
}
upvote
  flag
Wouldn't this trigger the clickout event also in the case when a child element of the container is clicked? – Jānis Elmeris

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 body that hides #menuscontainer only if the target (of the click) isn't a child of #menuscontainer.

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();
});

I don't think what you really need is to close the menu when the user clicks outside; what you need is for the menu to close when the user clicks anywhere at all on the page. If you click on the menu, or off the menu it should close right?

Finding no satisfactory answers above prompted me to write this blog post the other day. For the more pedantic, there are a number of gotchas to take note of:

  1. If you attach a click event handler to the body element at click time be sure to wait for the 2nd click before closing the menu, and unbinding the event. Otherwise the click event that opened the menu will bubble up to the listener that has to close the menu.
  2. If you use event.stopPropogation() on a click event, no other elements in your page can have a click-anywhere-to-close feature.
  3. Attaching a click event handler to the body element indefinitely is not a performant solution
  4. Comparing the target of the event, and its parents to the handler's creator assumes that what you want is to close the menu when you click off it, when what you really want is to close it when you click anywhere on the page.
  5. Listening for events on the body element will make your code more brittle. Styling as innocent as this would break it: body { margin-left:auto; margin-right: auto; width:960px;}
upvote
  flag
"If you click on the menu, or off the menu it should close right?" not always. Cancelling a click by dragging off an element will still trigger a document level click, but the intent would not be to continue closing the menu. There are also plenty of other types of dialogs that could use the "click-out" behavior that would allow for clicking internally. – zzzzBov

I found this method in some jQuery calendar plugin.

function ClickOutsideCheck(e)
{
  var el = e.target;
  var popup = $('.popup:visible')[0];
  if (popup==undefined)
    return true;

  while (true){
    if (el == popup ) {
      return true;
    } else if (el == document) {
      $(".popup").hide();
      return false;
    } else {
      el = $(el).parent()[0];
    }
  }
};

$(document).bind('mousedown.popup', ClickOutsideCheck);
$(document).click(function() {
    $(".overlay-window").hide();
});
$(".overlay-window").click(function() {
    return false;
});

If you click on the document, hide a given element, unless you click on that same element.

I did it like this in YUI 3:

// Detect the click anywhere other than the overlay element to close it.
Y.one(document).on('click', function (e) {
    if (e.target.ancestor('#overlay') === null && e.target.get('id') != 'show' && overlay.get('visible') == true) {
        overlay.hide();
    }
});

I am checking if ancestor is not the widget element container,
if target is not which open the widget/element,
if widget/element I want to close is already open (not that important).

As another poster said there are a lot of gotchas, especially if the element you are displaying (in this case a menu) has interactive elements. I've found the following method to be fairly robust:

$('#menuscontainer').click(function(event) {
    //your code that shows the menus fully

    //now set up an event listener so that clicking anywhere outside will close the menu
    $('html').click(function(event) {
        //check up the tree of the click target to check whether user has clicked outside of menu
        if ($(event.target).parents('#menuscontainer').length==0) {
            // your code to hide menu

            //this event listener has done its job so we can unbind it.
            $(this).unbind(event);
        }

    })
});

Here is my code:

// Listen to every click
$('html').click(function(event) {
    if ( $('#mypopupmenu').is(':visible') ) {
        if (event.target.id != 'click_this_to_show_mypopupmenu') {
            $('#mypopupmenu').hide();
        }
    }
});

// Listen to selector's clicks
$('#click_this_to_show_mypopupmenu').click(function() {

  // If the menu is visible, and you clicked the selector again we need to hide
  if ( $('#mypopupmenu').is(':visible') {
      $('#mypopupmenu').hide();
      return true;
  }

  // Else we need to show the popup menu
  $('#mypopupmenu').show();
});

This is my solution to this problem:

$(document).ready(function() {
  $('#user-toggle').click(function(e) {
    $('#user-nav').toggle();
    e.stopPropagation();
  });

  $('body').click(function() {
    $('#user-nav').hide(); 
  });

  $('#user-nav').click(function(e){
    e.stopPropagation();
  });
});
jQuery().ready(function(){
    $('#nav').click(function (event) {
        $(this).addClass('activ');
        event.stopPropagation();
    });

    $('html').click(function () {
        if( $('#nav').hasClass('activ') ){
            $('#nav').removeClass('activ');
        }
    });
});

Hook a click event listener on the document. Inside the event listener, you can look at the event object, in particular, the event.target to see what element was clicked:

$(document).click(function(e){
    if ($(e.target).closest("#menuscontainer").length == 0) {
        // .closest can help you determine if the element 
        // or one of its ancestors is #menuscontainer
        console.log("hide");
    }
});

This worked for me perfectly!!

$('html').click(function (e) {
    if (e.target.id == 'YOUR-DIV-ID') {
        //do something
    } else {
        //do something
    }
});
upvote
  flag
This solution worked well for what I was doing, where I still needed the click event to bubble up, thanks. – Mike

To be honest, I didn't like any of previous the solutions.

The best way to do this, is binding the "click" event to the document, and comparing if that click is really outside the element (just like Art said in his suggestion).

However, you'll have some problems there: You'll never be able to unbind it, and you cannot have an external button to open/close that element.

That's why I wrote this small plugin (click here to link), to simplify these tasks. Could it be simpler?

<a id='theButton' href="#">Toggle the menu</a><br/>
<div id='theMenu'>
    I should be toggled when the above menu is clicked,
    and hidden when user clicks outside.
</div>

<script>
$('#theButton').click(function(){
    $('#theMenu').slideDown();
});
$("#theMenu").dClickOutside({ ignoreList: $("#theButton") }, function(clickedObj){
    $(this).slideUp();
});
</script>

Just a warning that using this:

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

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

It prevents the Ruby on Rails UJS driver from working properly. For example, link_to 'click', '/url', :method => :delete will not work.

This might be a workaround:

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

$('#menucontainer').click(function(event){
  if (!$(event.target).data('method')) {
    event.stopPropagation();
  }
});

This should work:

$('body').click(function (event) {
    var obj = $(event.target);
    obj = obj['context']; // context : clicked element inside body
    if ($(obj).attr('id') != "menuscontainer" && $('#menuscontainer').is(':visible') == true) {
        //hide menu
    }
});
upvote
  flag
$("body > div:not(#dvid)").click(function (e) { //your code }); – Mahesh Gaikwad

If you are creating elements dynamically and using 'live' function to capture the events, stopPropagation might not work. In that case you can try stopImmediatePropagation.

 <div class="feedbackCont" onblur="hidefeedback();">
        <div class="feedbackb" onclick="showfeedback();" ></div>
        <div class="feedbackhide" tabindex="1"> </div>
 </div>

function hidefeedback(){
    $j(".feedbackhide").hide();
}

function showfeedback(){
    $j(".feedbackhide").show();
    $j(".feedbackCont").attr("tabindex",1).focus();
}

This is the simplest solution I came up with.

One more solution is here:

http://jsfiddle.net/zR76D/

Usage:

<div onClick="$('#menu').toggle();$('#menu').clickOutside(function() { $(this).hide(); $(this).clickOutside('disable'); });">Open / Close Menu</div>
<div id="menu" style="display: none; border: 1px solid #000000; background: #660000;">I am a menu, whoa is me.</div>

Plugin:

(function($) {
    var clickOutsideElements = [];
    var clickListener = false;

    $.fn.clickOutside = function(options, ignoreFirstClick) {
        var that = this;
        if (ignoreFirstClick == null) ignoreFirstClick = true;

        if (options != "disable") {
            for (var i in clickOutsideElements) {
                if (clickOutsideElements[i].element[0] == $(this)[0]) return this;
            }

            clickOutsideElements.push({ element : this, clickDetected : ignoreFirstClick, fnc : (typeof(options) != "function") ? function() {} : options });

            $(this).on("click.clickOutside", function(event) {
                for (var i in clickOutsideElements) {
                    if (clickOutsideElements[i].element[0] == $(this)[0]) {
                        clickOutsideElements[i].clickDetected = true;
                    }
                }
            });

            if (!clickListener) {
                if (options != null && typeof(options) == "function") {
                    $('html').click(function() {
                        for (var i in clickOutsideElements) {
                            if (!clickOutsideElements[i].clickDetected) {
                                clickOutsideElements[i].fnc.call(that);
                            }
                            if (clickOutsideElements[i] != null) clickOutsideElements[i].clickDetected = false;
                        }
                    });
                    clickListener = true;
                }
            }
        }
        else {
            $(this).off("click.clickoutside");
            for (var i = 0; i < clickOutsideElements.length; ++i) {
                if (clickOutsideElements[i].element[0] == $(this)[0]) {
                    clickOutsideElements.splice(i, 1);
                }
            }
        }

        return this;
    }
})(jQuery);

The broadest way to do this is to select everything on the web page except the element where you don't want clicks detected and bind the click event those when the menu is opened.

Then when the menu is closed remove the binding.

Use .stopPropagation to prevent the event from affecting any part of the menuscontainer.

$("*").not($("#menuscontainer")).bind("click.OutsideMenus", function ()
{
    // hide the menus

    //then remove all of the handlers
    $("*").unbind(".OutsideMenus");
});

$("#menuscontainer").bind("click.OutsideMenus", function (event) 
{
    event.stopPropagation(); 
});

The solutions here work fine when only one element is to be managed. If there are multiple elements, however, the problem is much more complicated. Tricks with e.stopPropagation() and all the others will not work.

I came up with a solution, and maybe it is not so easy, but it's better than nothing. Have a look:

$view.on("click", function(e) {

    if(model.isActivated()) return;

        var watchUnclick = function() {
            rootView.one("mouseleave", function() {
                $(document).one("click", function() {
                    model.deactivate();
                });
                rootView.one("mouseenter", function() {
                    watchUnclick();
                });
            });
        };
        watchUnclick();
        model.activate();
    });
upvote
  flag
I think onmouseenter and onmouseleave is the solution, check this out jsfiddle.net/1r73jm8m – Puerto AGP

Instead using flow interruption, blur/focus event or any other tricky technics, simply match event flow with element's kinship:

$(document).on("click.menu-outside", function(event){
    // Test if target and it's parent aren't #menuscontainer
    // That means the click event occur on other branch of document tree
    if(!$(event.target).parents().andSelf().is("#menuscontainer")){
        // Click outisde #menuscontainer
        // Hide the menus (but test if menus aren't already hidden)
    }
});

To remove click outside event listener, simply:

$(document).off("click.menu-outside");
upvote
  flag
To me this was the best solution. Used it in a callback after animation so I needed to the detach event somehwere. +1 – qwertzman
upvote
  flag
There is a redundant check here. if the element is indeed #menuscontainer you are still going through it's parents. you should first check that, and if it's not that element, then go up the DOM tree. – vsync
upvote
  flag
Right ! You can change the condition to if(!($(event.target).is("#menuscontainer") || $(event.target).parents().is("#menuscontainer"))){. It's a tiny optimization, but occurs only few times in your program lifetime : for each click, if the click.menu-outside event is registered. It's longer (+32 chars) and don't use method chaining – mems

The answer marked as the accepted answer does not take into account that you can have overlays over the element, like dialogs, popovers, datepickers, etc. Clicks in these should not hide the element.

I have made my own version that does take this into account. It's created as a KnockoutJS binding, but it can easily be converted to jQuery-only.

It works by the first query for all elements with either z-index or absolute position that are visible. It then hit tests those elements against the element I want to hide if click outside. If it's a hit I calculate a new bound rectangle which takes into account the overlay bounds.

ko.bindingHandlers.clickedIn = (function () {
    function getBounds(element) {
        var pos = element.offset();
        return {
            x: pos.left,
            x2: pos.left + element.outerWidth(),
            y: pos.top,
            y2: pos.top + element.outerHeight()
        };
    }

    function hitTest(o, l) {
        function getOffset(o) {
            for (var r = { l: o.offsetLeft, t: o.offsetTop, r: o.offsetWidth, b: o.offsetHeight };
                o = o.offsetParent; r.l += o.offsetLeft, r.t += o.offsetTop);
            return r.r += r.l, r.b += r.t, r;
        }

        for (var b, s, r = [], a = getOffset(o), j = isNaN(l.length), i = (j ? l = [l] : l).length; i;
            b = getOffset(l[--i]), (a.l == b.l || (a.l > b.l ? a.l <= b.r : b.l <= a.r))
                && (a.t == b.t || (a.t > b.t ? a.t <= b.b : b.t <= a.b)) && (r[r.length] = l[i]));
        return j ? !!r.length : r;
    }

    return {
        init: function (element, valueAccessor) {
            var target = valueAccessor();
            $(document).click(function (e) {
                if (element._clickedInElementShowing === false && target()) {
                    var $element = $(element);
                    var bounds = getBounds($element);

                    var possibleOverlays = $("[style*=z-index],[style*=absolute]").not(":hidden");
                    $.each(possibleOverlays, function () {
                        if (hitTest(element, this)) {
                            var b = getBounds($(this));
                            bounds.x = Math.min(bounds.x, b.x);
                            bounds.x2 = Math.max(bounds.x2, b.x2);
                            bounds.y = Math.min(bounds.y, b.y);
                            bounds.y2 = Math.max(bounds.y2, b.y2);
                        }
                    });

                    if (e.clientX < bounds.x || e.clientX > bounds.x2 ||
                        e.clientY < bounds.y || e.clientY > bounds.y2) {

                        target(false);
                    }
                }
                element._clickedInElementShowing = false;
            });

            $(element).click(function (e) {
                e.stopPropagation();
            });
        },
        update: function (element, valueAccessor) {
            var showing = ko.utils.unwrapObservable(valueAccessor());
            if (showing) {
                element._clickedInElementShowing = true;
            }
        }
    };
})();

This is a more general solution that allows multiple elements to be watched, and dynamically adding and removing elements from the queue.

It holds a global queue (autoCloseQueue) - an object container for elements that should be closed on outside clicks.

Each queue object key should be the DOM Element id, and the value should be an object with 2 callback functions:

 {onPress: someCallbackFunction, onOutsidePress: anotherCallbackFunction}

Put this in your document ready callback:

window.autoCloseQueue = {}  

$(document).click(function(event) {
    for (id in autoCloseQueue){
        var element = autoCloseQueue[id];
        if ( ($(e.target).parents('#' + id).length) > 0) { // This is a click on the element (or its child element)
            console.log('This is a click on an element (or its child element) with  id: ' + id);
            if (typeof element.onPress == 'function') element.onPress(event, id);
        } else { //This is a click outside the element
            console.log('This is a click outside the element with id: ' + id);
            if (typeof element.onOutsidePress == 'function') element.onOutsidePress(event, id); //call the outside callback
            delete autoCloseQueue[id]; //remove the element from the queue
        }
    }
});

Then, when the DOM element with id 'menuscontainer' is created, just add this object to the queue:

window.autoCloseQueue['menuscontainer'] = {onOutsidePress: clickOutsideThisElement}

I ended up doing something like this:

$(document).on('click', 'body, #msg_count_results .close',function() {
    $(document).find('#msg_count_results').remove();
});
$(document).on('click','#msg_count_results',function(e) {
    e.preventDefault();
    return false;
});

I have a close button within the new container for end users friendly UI purposes. I had to use return false in order to not go through. Of course, having an A HREF on there to take you somewhere would be nice, or you could call some ajax stuff instead. Either way, it works ok for me. Just what I wanted.

Try this code:

if ($(event.target).parents().index($('#searchFormEdit')) == -1 &&
    $(event.target).parents().index($('.DynarchCalendar-topCont')) == -1 &&
    (_x < os.left || _x > (os.left + 570) || _y < os.top || _y > (os.top + 155)) &&
    isShowEditForm) {

    setVisibleEditForm(false);
}

You can set a tabindex to the DOM element. This will trigger a blur event when the user click outside the DOM element.

Demo

<div tabindex="1">
    Focus me
</div>

document.querySelector("div").onblur = function(){
   console.log('clicked outside')
}
document.querySelector("div").onfocus = function(){
   console.log('clicked inside')
}

This will toggle the Nav menu when you click on/off the element.

$(document).on('click', function(e) {
    var elem = $(e.target).closest('#menu'),
    box = $(e.target).closest('#nav');
 if (elem.length) {
    e.preventDefault();
    $('#nav').toggle();
  } else if (!box.length) {
    $('#nav').hide();
 }
});



<li id="menu"><a></a></li>
<ul id="nav" >  //Nav will toggle when you Click on Menu(it can be an icon in this example)
        <li class="page"><a>Page1</a></li>
        <li class="page"><a>Pag2</a></li>
        <li class="page"><a>Page3</a></li>            
        <li class="page"><a>Page4</a></li>
</ul>

Standard HTML:

Surround the menus by a <label> and fetch focus state changes.

http://jsfiddle.net/bK3gL/

Plus: you can unfold the menu by Tab.

upvote
  flag
plus: you can unfold the menu by <kbd>tab</kbd> – Peter Rader

As a wrapper to this great answer from Art, and to use the syntax originally requested by OP, here's a jQuery extension that can record wether a click occured outside of a set element.

$.fn.clickOutsideThisElement = function (callback) {
    return this.each(function () {
        var self = this;
        $(document).click(function (e) {
            if (!$(e.target).closest(self).length) {
                callback.call(self, e)
            }
        })
    });
};

Then you can call like this:

$("#menuscontainer").clickOutsideThisElement(function() {
    // handle menu toggle
});

Here's a demo in fiddle

As a variant:

var $menu = $('#menucontainer');
$(document).on('click', function (e) {

    // If element is opened and click target is outside it, hide it
    if ($menu.is(':visible') && !$menu.is(e.target) && !$menu.has(e.target).length) {
        $menu.hide();
    }
});

It has no problem with stopping event propagation and 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.

For touch devices like iPad and iPhone we can use this code:

$(document).on('touchstart', function (event) {
    var container = $("YOUR CONTAINER SELECTOR");

    if (!container.is(e.target) &&            // If the target of the click isn't the container...
        container.has(e.target).length === 0) // ... nor a descendant of the container
    {
        container.hide();
    }
});

Using not():

$("#id").not().click(function() {
    alert('Clicked other that #id');
});
3 upvote
  flag
This doesn't work. not() removes an element from the list of selected elements (api.jquery.com/not). Without a selector as it's parameter it does nothing, thus returning $('#id') which is exactly the opposite of what we're trying to accomplish. – flu
$("body > div:not(#dvid)").click(function (e) {
    //your code
}); 
upvote
  flag
Add a click handler to every other element? You will kill performance. There are much better ways to do this. – bguiz
upvote
  flag
This add a click handler to all div element except #dvid, it is very expensive for the page and if you have some div inside #divid also they have the handler and it can be an unexpected effect – Migio B
$("html").click(function(){
    if($('#info').css("opacity")>0.9) {
        $('#info').fadeOut('fast');
    }
});

Solution1

Instead of using event.stopPropagation() which can have some side affects, just define a simple flag variable and add one if condition. I tested this and worked properly without any side affects of stopPropagation:

var flag = "1";
$('#menucontainer').click(function(event){
    flag = "0"; // flag 0 means click happened in the area where we should not do any action
});

$('html').click(function() {
    if(flag != "0"){
        // Hide the menus if visible
    }
    else {
        flag = "1";
    }
});

Solution2

With just a simple if condition:

$(document).on('click', function(event){
    var container = $("#menucontainer");
    if (!container.is(event.target) &&            // If the target of the click isn't the container...
        container.has(event.target).length === 0) // ... nor a descendant of the container
    {
        // Do whatever you want to do when click is outside the element
    }
});
upvote
  flag
I used this solution with a boolean flag and it's good also with a articulated DOm and also if inside #menucontainer there are a lot of other elements – Migio B

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>

We implemented a solution, partly based off a comment from a user above, which works perfectly for us. We use it to hide a search box / results when clicking outside those elements, excluding the element that originally.

// HIDE SEARCH BOX IF CLICKING OUTSIDE
$(document).click(function(event){ 
    // IF NOT CLICKING THE SEARCH BOX OR ITS CONTENTS OR SEARCH ICON 
    if ($("#search-holder").is(":visible") && !$(event.target).is("#search-holder *, #search")) {
        $("#search-holder").fadeOut('fast');
        $("#search").removeClass('active');
    }
});

It checks if the search box is already visible first also, and in our case, it's also removing an active class on the hide/show search button.

Upvote for the most popular answer, but add

&& (e.target != $('html').get(0)) // ignore the scrollbar

so, a click on a scroll bar does not [hide or whatever] your target element.

This is a classic case of where a tweak to the HTML would be a better solution. Why not set the click on the elements which don't contain the menu item? Then you don't need to add the propagation.

$('.header, .footer, .main-content').click(function() {
//Hide the menus if visible
});
upvote
  flag
It's bad idea to do this because if later on you decide to add some kind of footer2 or something else in between you will have to remember to add it also. $('html').click() easier and makes it work in every case. – Marcio

Outside click plugin!

Usage:

$('.target-element').outsideClick(function(event){
    //code that fires when user clicks outside the element
    //event = the click event
    //$(this) = the '.target-element' that is firing this function 
}, '.excluded-element')

The code for it:

(function($) {

//when the user hits the escape key, it will trigger all outsideClick functions
$(document).on("keyup", function (e) {
    if (e.which == 27) $('body').click(); //escape key
});

//The actual plugin
$.fn.outsideClick = function(callback, exclusions) {
    var subject = this;

    //test if exclusions have been set
    var hasExclusions = typeof exclusions !== 'undefined';

    //switches click event with touch event if on a touch device
    var ClickOrTouchEvent = "ontouchend" in document ? "touchend" : "click";

    $('body').on(ClickOrTouchEvent, function(event) {
        //click target does not contain subject as a parent
        var clickedOutside = !$(event.target).closest(subject).length;

        //click target was on one of the excluded elements
        var clickedExclusion = $(event.target).closest(exclusions).length;

        var testSuccessful;

        if (hasExclusions) {
            testSuccessful = clickedOutside && !clickedExclusion;
        } else {
            testSuccessful = clickedOutside;
        }

        if(testSuccessful) {
            callback.call(subject, event);
        }
    });

    return this;
};

}(jQuery));

Adapted from this answer //allinonescript.com/a/3028037/1611058

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>
5 upvote
  flag
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. – dbarth
upvote
  flag
I am trying to get the third solution going with multiple elements document.getElementsByClassName, if someone has a clue please share. – lowtechsun
upvote
  flag
@lowtechsun You'd have to loop through to check each. – Donnie D'Amato
    $('#menucontainer').click(function(e){
        e.stopPropagation();
     });

    $(document).on('click',  function(e){
        // code
    });

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/

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

$('#menucontainer').click(function(event){
    event.stopPropagation();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
 <button id='#menucontainer'>Ok</button> 
</html>

Have a try of this:

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

https://jsfiddle.net/4cj4jxy0/

But note that this cannot work if the click event cannot reach the html tag. (Maybe other elements have stopPropagation()).

Subscribe capturing phase of click to handle click on elements which call preventDefault.
Retrigger it on document element using the other name click-anywhere.

document.addEventListener('click', function (event) {
  event = $.event.fix(event);
  event.type = 'click-anywhere';
  $document.trigger(event);
}, true);

Then where you need click outside functionality subscribe on click-anywhere event on document and check if the click was outside of the element you are interested in:

$(document).on('click-anywhere', function (event) {
  if (!$(event.target).closest('#smth').length) {
    // Do anything you need here
  }
});

Some notes:

  • You have to use document as it would be a perfomance fault to trigger event on all elements outside of which the click occured.

  • This functionality can be wrapped into special plugin, which calls some callback on outside click.

  • You can't subscribe capturing phase using jQuery itself.

  • You don't need document load to subscribe as subscription is on document, even not on its body, so it exists always independently ащкь script placement and load status.

upvote
  flag
+1 for mentioning the capture phase -- certainly reduces the risk of stopPropagation() killing things (depending on where you attach the listener). – Nate Whittaker

For easier use, and more expressive code, I created a jQuery plugin for this:

$('div.my-element').clickOut(function(target) { 
    //do something here... 
});

Note: target is the element the user actually clicked. But callback is still executed in the context of the original element, so you can utilize this as you'd expect in a jQuery callback.

Plugin:

$.fn.clickOut = function (parent, fn) {
    var context = this;
    fn = (typeof parent === 'function') ? parent : fn;
    parent = (parent instanceof jQuery) ? parent : $(document);

    context.each(function () {
        var that = this;
        parent.on('click', function (e) {
            var clicked = $(e.target);
            if (!clicked.is(that) && !clicked.parents().is(that)) {
                if (typeof fn === 'function') {
                    fn.call(that, clicked);
                }
            }
        });

    });
    return context;
};

By default, the click event listener is placed on the document. However, if you want to limit the event listener scope, you can pass in a jQuery object representing a parent level element that will be the top parent at which clicks will be listened to. This prevents unnecessary document level event listeners. Obviously, it won't work unless the parent element supplied is a parent of your initial element.

Use like so:

$('div.my-element').clickOut($('div.my-parent'), function(target) { 
    //do something here...
});
upvote
  flag
thanks for the plugin, but might I suggest one small alteration, I would change the line fn.call(context, clicked); to fn.call(that, clicked);. That way you get the dom object back that isn't clicked instead of a list of objects that listen to the event – furrie
upvote
  flag
Good catch, I will update the answer. – Matt Goodwin
$(document).on('click.menu.hide', function(e){
  if ( !$(e.target).closest('#my_menu').length ) {
    $('#my_menu').find('ul').toggleClass('active', false);
  }
});

$(document).on('click.menu.show', '#my_menu li', function(e){
  $(this).find('ul').toggleClass('active');
});
div {
  float: left;
}

ul {
  padding: 0;
  position: relative;
}
ul li {
  padding: 5px 25px 5px 10px;
  border: 1px solid silver;
  cursor: pointer;
  list-style: none;
  margin-top: -1px;
  white-space: nowrap;
}
ul li ul:before {
  margin-right: -20px;
  position: absolute;
  top: -17px;
  right: 0;
  content: "\25BC";
}
ul li ul li {
  visibility: hidden;
  height: 0;
  padding-top: 0;
  padding-bottom: 0;
  border-width: 0 0 1px 0;
}
ul li ul li:last-child {
  border: none;
}
ul li ul.active:before {
  content: "\25B2";
}
ul li ul.active li {
  display: list-item;
  visibility: visible;
  height: inherit;
  padding: 5px 25px 5px 10px;
}
<script src="https://code.jquery.com/jquery-2.1.4.js"></script>
<div>
  <ul id="my_menu">
    <li>Menu 1
      <ul>
        <li>subMenu 1</li>
        <li>subMenu 2</li>
        <li>subMenu 3</li>
        <li>subMenu 4</li>
      </ul>
    </li>
    <li>Menu 2
      <ul>
        <li>subMenu 1</li>
        <li>subMenu 2</li>
        <li>subMenu 3</li>
        <li>subMenu 4</li>
      </ul>
    </li>
    <li>Menu 3</li>
    <li>Menu 4</li>
    <li>Menu 5</li>
    <li>Menu 6</li>
  </ul>
</div>

Here is jsbin version http://jsbin.com/xopacadeni/edit?html,css,js,output

How to detect a click outside an element?

The reason that this question is so popular and has so many answers is that it is deceptively complex. After almost eight years and dozens of answers, I am genuinely surprised to see how little care has been given to accessibility.

I would like to hide these elements when the user clicks outside the menus' area.

This is a noble cause and is the actual issue. The title of the question—which is what most answers appear to attempt to address—contains an unfortunate red herring.

Hint: it's the word "click"!

You don't actually want to bind click handlers.

If you're binding click handlers to close the dialog, you've already failed. The reason you've failed is that not everyone triggers click events. Users not using a mouse will be able to escape your dialog (and your pop-up menu is arguably a type of dialog) by pressing Tab, and they then won't be able to read the content behind the dialog without subsequently triggering a click event.

So let's rephrase the question.

How does one close a dialog when a user is finished with it?

This is the goal. Unfortunately, now we need to bind the userisfinishedwiththedialog event, and that binding isn't so straightforward.

So how can we detect that a user has finished using a dialog?

focusout event

A good start is to determine if focus has left the dialog.

Hint: be careful with the blur event, blur doesn't propagate if the event was bound to the bubbling phase!

jQuery's focusout will do just fine. If you can't use jQuery, then you can use blur during the capturing phase:

element.addEventListener('blur', ..., true);
//                       use capture: ^^^^

Also, for many dialogs you'll need to allow the container to gain focus. Add tabindex="-1" to allow the dialog to receive focus dynamically without otherwise interrupting the tabbing flow.

$('a').on('click', function () {
  $(this.hash).toggleClass('active').focus();
});

$('div').on('focusout', function () {
  $(this).removeClass('active');
});
div {
  display: none;
}
.active {
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#example">Example</a>
<div id="example" tabindex="-1">
  Lorem ipsum <a href="http://example.com">dolor</a> sit amet.
</div>


If you play with that demo for more than a minute you should quickly start seeing issues.

The first is that the link in the dialog isn't clickable. Attempting to click on it or tab to it will lead to the dialog closing before the interaction takes place. This is because focusing the inner element triggers a focusout event before triggering a focusin event again.

The fix is to queue the state change on the event loop. This can be done by using setImmediate(...), or setTimeout(..., 0) for browsers that don't support setImmediate. Once queued it can be cancelled by a subsequent focusin:

$('.submenu').on({
  focusout: function (e) {
    $(this).data('submenuTimer', setTimeout(function () {
      $(this).removeClass('submenu--active');
    }.bind(this), 0));
  },
  focusin: function (e) {
    clearTimeout($(this).data('submenuTimer'));
  }
});

$('a').on('click', function () {
  $(this.hash).toggleClass('active').focus();
});

$('div').on({
  focusout: function () {
    $(this).data('timer', setTimeout(function () {
      $(this).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this).data('timer'));
  }
});
div {
  display: none;
}
.active {
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#example">Example</a>
<div id="example" tabindex="-1">
  Lorem ipsum <a href="http://example.com">dolor</a> sit amet.
</div>

The second issue is that the dialog won't close when the link is pressed again. This is because the dialog loses focus, triggering the close behavior, after which the link click triggers the dialog to reopen.

Similar to the previous issue, the focus state needs to be managed. Given that the state change has already been queued, it's just a matter of handling focus events on the dialog triggers:

This should look familiar
$('a').on({
  focusout: function () {
    $(this.hash).data('timer', setTimeout(function () {
      $(this.hash).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this.hash).data('timer'));  
  }
});

$('a').on('click', function () {
  $(this.hash).toggleClass('active').focus();
});

$('div').on({
  focusout: function () {
    $(this).data('timer', setTimeout(function () {
      $(this).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this).data('timer'));
  }
});

$('a').on({
  focusout: function () {
    $(this.hash).data('timer', setTimeout(function () {
      $(this.hash).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this.hash).data('timer'));  
  }
});
div {
  display: none;
}
.active {
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#example">Example</a>
<div id="example" tabindex="-1">
  Lorem ipsum <a href="http://example.com">dolor</a> sit amet.
</div>


Esc key

If you thought you were done by handling the focus states, there's more you can do to simplify the user experience.

This is often a "nice to have" feature, but it's common that when you have a modal or popup of any sort that the Esc key will close it out.

keydown: function (e) {
  if (e.which === 27) {
    $(this).removeClass('active');
    e.preventDefault();
  }
}

$('a').on('click', function () {
  $(this.hash).toggleClass('active').focus();
});

$('div').on({
  focusout: function () {
    $(this).data('timer', setTimeout(function () {
      $(this).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this).data('timer'));
  },
  keydown: function (e) {
    if (e.which === 27) {
      $(this).removeClass('active');
      e.preventDefault();
    }
  }
});

$('a').on({
  focusout: function () {
    $(this.hash).data('timer', setTimeout(function () {
      $(this.hash).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this.hash).data('timer'));  
  }
});
div {
  display: none;
}
.active {
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#example">Example</a>
<div id="example" tabindex="-1">
  Lorem ipsum <a href="http://example.com">dolor</a> sit amet.
</div>


If you know you have focusable elements within the dialog, you won't need to focus the dialog directly. If you're building a menu, you could focus the first menu item instead.

click: function (e) {
  $(this.hash)
    .toggleClass('submenu--active')
    .find('a:first')
    .focus();
  e.preventDefault();
}

$('.menu__link').on({
  click: function (e) {
    $(this.hash)
      .toggleClass('submenu--active')
      .find('a:first')
      .focus();
    e.preventDefault();
  },
  focusout: function () {
    $(this.hash).data('submenuTimer', setTimeout(function () {
      $(this.hash).removeClass('submenu--active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this.hash).data('submenuTimer'));  
  }
});

$('.submenu').on({
  focusout: function () {
    $(this).data('submenuTimer', setTimeout(function () {
      $(this).removeClass('submenu--active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this).data('submenuTimer'));
  },
  keydown: function (e) {
    if (e.which === 27) {
      $(this).removeClass('submenu--active');
      e.preventDefault();
    }
  }
});
.menu {
  list-style: none;
  margin: 0;
  padding: 0;
}
.menu:after {
  clear: both;
  content: '';
  display: table;
}
.menu__item {
  float: left;
  position: relative;
}

.menu__link {
  background-color: lightblue;
  color: black;
  display: block;
  padding: 0.5em 1em;
  text-decoration: none;
}
.menu__link:hover,
.menu__link:focus {
  background-color: black;
  color: lightblue;
}

.submenu {
  border: 1px solid black;
  display: none;
  left: 0;
  list-style: none;
  margin: 0;
  padding: 0;
  position: absolute;
  top: 100%;
}
.submenu--active {
  display: block;
}

.submenu__item {
  width: 150px;
}

.submenu__link {
  background-color: lightblue;
  color: black;
  display: block;
  padding: 0.5em 1em;
  text-decoration: none;
}

.submenu__link:hover,
.submenu__link:focus {
  background-color: black;
  color: lightblue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="menu">
  <li class="menu__item">
    <a class="menu__link" href="#menu-1">Menu 1</a>
    <ul class="submenu" id="menu-1" tabindex="-1">
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#1">Example 1</a></li>
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#2">Example 2</a></li>
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#3">Example 3</a></li>
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#4">Example 4</a></li>
    </ul>
  </li>
  <li class="menu__item">
    <a  class="menu__link" href="#menu-2">Menu 2</a>
    <ul class="submenu" id="menu-2" tabindex="-1">
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#1">Example 1</a></li>
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#2">Example 2</a></li>
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#3">Example 3</a></li>
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#4">Example 4</a></li>
    </ul>
  </li>
</ul>
lorem ipsum <a href="http://example.com/">dolor</a> sit amet.


WAI-ARIA Roles and Other Accessibility Support

This answer hopefully covers the basics of accessible keyboard and mouse support for this feature, but as it's already quite sizable I'm going to avoid any discussion of WAI-ARIA roles and attributes, however I highly recommend that implementers refer to the spec for details on what roles they should use and any other appropriate attributes.

11 upvote
  flag
This is the most complete answer, with explanations and accessibility in mind. I think this should be the accepted answer since most of the other answers only handle click and are just code snippet dropped without any explanations. – Cyrille
upvote
  flag
@Cyrille, thank you for your kind words. You'll have to convince the OP to change the accepted answer if you feel so strongly. – zzzzBov
2 upvote
  flag
Amazing, well explained. I just used this approach on a React Component and worked perfectly thanks – David Lavieri
upvote
  flag
@zzzzBov thanks for the in deep answer, i am trying to achieve it in vanilla JS , and i am a bit lost with all the jquery stuff. is there anything similar in vanilla js? – HendrikEng
upvote
  flag
@Karle, you're going to have to ask a more specific question. jQuery is written in vanilla JS, so in that regard: yes absolutely there are ways of doing this without jQuery. I can't imagine that answer helps you in any way. If you're looking for me to write you a jQuery-free version, then I'm going to have to decline in advance. – zzzzBov
1 upvote
  flag
@zzzzBov no i am not looking for you to write a jQuery-free version of course, i will try to do it and i guess the best is to ask a new question here if i really get stuck. Thanks a lot again. – HendrikEng
1 upvote
  flag
I'd like to add an edge case that prevented this from working for me that people should be aware of. I was creating a popup for authentication functions such as login and signup. I also happen to be an avid 1Password user. When I clicked "Login" the 1Password extension appeared asking if I want to save the credentials. This meant the popup closed as the associated focusIn/Out events were fired. In my setup I noticed that the element that took focus otherwise was always body, so I wrapped the logic in a check on the activeElement.tagName === 'BODY'. Best I could do I think – horyd
1 upvote
  flag
While this is a great method for detecting clicking out of custom dropdowns or other inputs, it should never be the preferred method for modals or popups for two reasons. The modal will close when the user switches to another tab or window, or opens a context menu, which is really f* annoying. Additionally, the 'click' event fires on mouse up, while the 'focusout' event triggers the instant you press mouse down. Normally, a button only performs an action if you press the mouse button down and then let go. The proper, accessible way to do this for modals is to add a tabbable close button. – Kevin
upvote
  flag
@Kevin, "The modal will close when the user switches to another tab or window, or opens a context menu" there are events that can be used to handle these cases. "which is really f* annoying." I find modals to be annoying in general, almost never worth the hassle. " the 'click' event fires on mouse up...Normally, a button only performs an action if you press the mouse button down and then let go." I agree that this solution doesn't technically wait for a click, and that it's therefor unable to cancel the "click" via a drag action. In practice I haven't seen it pose a problem. – zzzzBov
upvote
  flag
@Kevin, "The proper, accessible way to do this for modals is to add a tabbable close button." I absolutely agree that most modals should have a close button. There are certain cases where close buttons are impractical. I mostly use this technique to create link dropdowns which don't typically need dedicated close buttons, in much the same way that a <select> element doesn't need a close button. In any event, I've been attempting to answer the question generically, and am not writing this as a comprehensive guide for how to build modals. – zzzzBov
upvote
  flag
Do we really need e.preventDefault(); for ESC keyboard event? – KimchiMan

To hide fileTreeClass if clicked outside of it

 jQuery(document).mouseup(function (e) {
            var container = $(".fileTreeClass");
            if (!container.is(e.target) // if the target of the click isn't the container...
                && container.has(e.target).length === 0) // ... nor a descendant of the container
            {
                container.hide();
            }
        });

Simple plugin:

$.fn.clickOff = function(callback, selfDestroy) {
    var clicked = false;
    var parent = this;
    var destroy = selfDestroy || true;

    parent.click(function() {
        clicked = true;
    });

    $(document).click(function(event) {
        if (!clicked && parent.is(':visible')) {
            if(callback) callback.call(parent, event)
        }
        if (destroy) {
            //parent.clickOff = function() {};
            //parent.off("click");
            //$(document).off("click");
            parent.off("clickOff");
        }
        clicked = false;
    });
};

Use:

$("#myDiv").clickOff(function() {
   alert('clickOff');
});

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.

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

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/

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.

$(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.

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;
}
<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
  }
});

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';
}

});

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;
}

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();
    }
});

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;
})
upvote
  flag
"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. – MortenMoulder
upvote
  flag
@MortenMoulder: Ya. It's just for attention even though it is actually ES6. But just look at the solution. I think it is good. – Duannx

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;
}

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>

$('#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);
      });

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.