我有一些HTML菜单,当用户单击这些菜单的头部时,我会完全显示这些菜单。当用户在菜单区域外单击时,我希望隐藏这些元素。

jQuery是否可以实现这样的功能?

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

当前回答

我在以下方面取得了成功:

var $menuscontainer = ...;

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

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

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

逻辑是:当显示#菜单容器时,仅当(单击的)目标不是它的子对象时,才将一个单击处理程序绑定到隐藏#菜单容器的主体。

其他回答

作为一种变体:

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

它在停止事件传播方面没有问题,并且更好地支持同一页面上的多个菜单,其中当第一个菜单打开时,单击第二个菜单将使stopPropagation解决方案中的第一个菜单保持打开状态。

为了更易于使用和表达代码,我为此创建了一个jQuery插件:

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

注意:target是用户实际单击的元素。但回调仍然在原始元素的上下文中执行,所以您可以像在jQuery回调中预期的那样使用它。

插件:

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

默认情况下,单击事件侦听器放置在文档上。但是,如果要限制事件侦听器范围,可以传入一个jQuery对象,该对象表示父级元素,该元素将是侦听单击的顶级父级元素。这可以防止不必要的文档级事件侦听器。显然,除非提供的父元素是初始元素的父元素,否则它将无法工作。

使用方式如下:

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

简单插件:

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

检查窗口单击事件目标(它应该传播到窗口,只要它没有在其他地方被捕获),并确保它不是任何菜单元素。如果不是,那么你就在菜单之外。

或者检查单击的位置,看看它是否包含在菜单区域中。

解决方案1

不要使用event.stopPropagation(),因为它会产生一些副作用,只需定义一个简单的标志变量并添加一个if条件。我对此进行了测试,工作正常,没有任何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";
    }
});

解决方案2

只有一个简单的if条件:

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