div focusout not working

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

Replay

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

Warning, if using this technique, be aware of the dangers of stopping propagation.

You can hook up to the click event of document and then make sure #menucontainer is not an ancestor or the target of the clicked element (jQuery.closest(), jQuery.is()).

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 && !$(event.target).is('#menucontainer')) {
        if($('#menucontainer').is(":visible")) {
            $('#menucontainer').hide()
        }
    }
})

Hope it helps.

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

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

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

$("#menuscontainer").click(function() {
    $(this).focus();
});
$("#menuscontainer").blur(function(){
    $(this).hide();
});

Works for me just fine.

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.

This worked for me perfectly!!

$('html').click(function (e) {
    if (e.target.id == 'YOUR-DIV-ID') {
        //do something
    } else {
        //do something
    }
});

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

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.

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

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.

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

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.

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

    })
});

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

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

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

This worked perfectly fine in time for me :

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

Thanks very much!

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

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

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

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 did like this in YUI3:

// detect the click anywhere other than 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).

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.

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

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

After research i have found three working solutions (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>

Here is my code:

// listen to every clicks
$('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();
});

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

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>

I spend a lot of time making it to be reliable, hope you enjoy.

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

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

I came up with a solution, maybe 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();
});

Hope it helps someone some day :)

Category: javascript Time: 2008-09-30 Views: 0

Related post

iOS development

Android development

Python development

JAVA development

Development language

PHP development

Ruby development

search

Front-end development

Database

development tools

Open Platform

Javascript development

.NET development

cloud computing

server

Copyright (C) avrocks.com, All Rights Reserved.

processed in 0.101 (s). 12 q(s)