Click outside menu to close in jquery

So I have a drop-down menu that shows on a click, as per business requirements. The menu becomes hidden again after you mouse away from it.

But now I am being asked to have it stay in place until user clicks anywhere on the document. How can this be accomplished?

This is a simplified version of what I have now:

$(document).ready(function() {
  $("ul.opMenu li").click(function(){
   $('#MainOptSubMenu',this).css('visibility', 'visible');
  });

  $("ul.opMenu li").mouseleave(function(){
      $('#MainOptSubMenu',this).css('visibility', 'hidden');
  });
});



<ul  class="opMenu">
  <li id="footwo" class="">
    <span id="optImg" style="display: inline-block;"> <img src="http://localhost.vmsinfo.com:8002/insight/images/options-hover2.gif"/> </span>
      <ul id="MainOptSubMenu" style="visibility: hidden; top: 25px; border-top: 0px solid rgb(217, 228, 250); background-color: rgb(217, 228, 250); padding-bottom: 15px;">
        <li>some</li>
       <li>nav</li>
       <li>links</li>
       </ul>
    </li>
</ul> 

I tried something like this $('document[id!=MainOptSubMenu]').click(function() thinking it would trigger on anything that wasnt the menu, but it didnt work.


Take a look at the approach this question used:

How do I detect a click outside an element?

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.
$('html').click(function() {
  //Hide the menus if visible
});

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


The answer is right, but it will add a listener that will be triggered every time a click occurs on your page. To avoid that, you can add the listener for just one time :

$('a#menu-link').on('click', function(e) {
    e.preventDefault();
    e.stopPropagation();

    $('#menu').toggleClass('open');

    $(document).one('click', function closeMenu (e){
        if($('#menu').has(e.target).length === 0){
            $('#menu').removeClass('open');
        } else {
            $(document).one('click', closeMenu);
        }
    });
});

Edit: if you want to avoid the stopPropagation() on the initial button you can use this

var $menu = $('#menu');

$('a#menu-link').on('click', function(e) {
    e.preventDefault();

    if (!$menu.hasClass('active')) {
        $menu.addClass('active');

        $(document).one('click', function closeTooltip(e) {
            if ($menu.has(e.target).length === 0 && $('a#menu-link').has(e.target).length === 0) {
                $menu.removeClass('active');
            } else if ($menu.hasClass('active')) {
                $(document).one('click', closeTooltip);
            }
        });
    } else {
        $menu.removeClass('active');
    }
});

The stopPropagation options are bad because they can interfere with other event handlers including other menus that might have attached close handlers to the HTML element.

Here is a simple solution based on user2989143's answer:

$('html').click(function(event) {
    if ($(event.target).closest('#menu-container, #menu-activator').length === 0) {
        $('#menu-container').hide();
    }
});

If using a plugin is ok in you case, then I suggest Ben Alman's clickoutside plugin located here:

its usage is as simple as this:

$('#menu').bind('clickoutside', function (event) {
    $(this).hide();
});

hope this helps.


2 options that you can investigate:

  • On showing of the menu, place a large empty DIV behind it covering up the rest of the page and give that an on-click event to close the menu (and itself). This is akin to the methods used with lightboxes where clicking on the background closes the lightbox
  • On showing of the menu, attach a one-time click event handler on the body that closes the menu. You use jQuery's '.one()' for this.