How do I detect a click outside an element?

2018-12-30 22:23发布

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

Is something like this possible with jQuery?

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

30条回答
不流泪的眼
2楼-- · 2018-12-30 22:45

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;}
查看更多
忆尘夕之涩
3楼-- · 2018-12-30 22:45

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.

查看更多
倾城一夜雪
4楼-- · 2018-12-30 22:45

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();
  });
});
查看更多
残风、尘缘若梦
5楼-- · 2018-12-30 22:46

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/

查看更多
残风、尘缘若梦
6楼-- · 2018-12-30 22:49

I've had success with something like this:

var $menuscontainer = ...;

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

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

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

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

查看更多
有味是清欢
7楼-- · 2018-12-30 22:49

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...
});
查看更多
登录 后发表回答