views:

13035

answers:

11

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
});
+5  A: 

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.

Chris MacDonald
+55  A: 

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.

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

 $('#menucontainer').click(function(event){
     event.stopPropagation();
 });
Eran Galperin
event.stopPropagation();beautiful :)
vsync
I prefer to bind the document to the click event, then unbind the event when needed. its more efficient.
vsync
This breaks standard behaviour of many things, including buttons and links, contained within #menucontainer. I am surprised this answer is so popular.
Art
I have posted an alternative solution, which does not break he behaviour http://stackoverflow.com/questions/152975/how-to-detect-a-click-outside-an-element/3028037#3028037
Art
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
+7  A: 

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) {
  $('body').one(function() {
    // Hide the menus
  });

  event.stopPropagation();
});

More information on jQuery's one() function

Joe Lencioni
but then if you click on the menu itself, then outside, it won't work :)
vsync
+3  A: 

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

if(!$(event.target).is('#foo'))
{
 // hide menu
}
Dennis
A: 

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;}
+2  A: 
$("#menuscontainer").click(function() {
    $(this).focus();
});
$("#menuscontainer").blur(function(){
    $(this).hide();
});

Works for me just fine.

This is the one I'd use. It may not be perfect, but as a hobby programmer, its simple enough to understand clearly.
kevtrout
A: 

Attach a click event to the document which closes the window. Attaching it to the body only attaches an event to how far the page flows vertically. I used Eran's solution originally but it didn't work for me since my page was very short vertically. Attach a separate click event to the window which stops propagation to the document itself.

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

 $('#menucontainer').click(function(e){ 
     e.stopPropagation(); 
 });
+2  A: 

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.

Wolfram
A: 

This worked perfectly fine intime for me

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

Thanks very much

govind
A: 

You can hook up to the click event of document and then checking whether clicked element is an ancestor of menucontainer.

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

$(document).click(function(event) { 
    if($(event.target).parents().index($('#menucontainer')) == -1) {
        if($('#menucontainer').is(":visible")) {
            $('#menucontainer').hide();
        }
    }        
}

Hope it helps.

Art
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
@Eran Galperin you are right, I am sorry. Initially I reproduced the logic in Prototype and apparently it works differently there. Need to investigate that, though...
Art
A: 

function:

$(function() {
    debugger;
    $.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();
}
Neco