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

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

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

当前回答

为了更易于使用和表达代码,我为此创建了一个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...
});

其他回答

对于某些人来说,这可能是一个更好的解决方案。

$(".menu_link").click(function(){
    // show menu code
});

$(".menu_link").mouseleave(function(){
    //hide menu code, you may add a timer for 3 seconds before code to be run
});

我知道mouseleave不仅意味着在外面单击,还意味着离开该元素的区域。

一旦菜单本身位于menu_link元素内,那么单击或移动菜单本身应该不会有问题。

我不认为你真正需要的是当用户在外面点击时关闭菜单;您需要的是当用户单击页面上的任何位置时关闭菜单。如果你点击菜单,或者离开菜单,它应该会关闭吗?

发现上面没有令人满意的答案,促使我前几天写了这篇博文。对于更迂腐的人,有很多陷阱需要注意:

如果在单击时将单击事件处理程序附加到body元素,请确保在关闭菜单并解除事件绑定之前等待第二次单击。否则,打开菜单的单击事件将弹出到必须关闭菜单的侦听器。若在单击事件上使用event.stopProposition(),则页面中的其他元素都不能在任何位置单击以关闭功能。无限期地将click事件处理程序附加到body元素不是一个高性能的解决方案将事件的目标及其父级与处理程序的创建者进行比较时,假设您想要的是在单击关闭菜单时关闭菜单,而真正想要的是当您单击页面上的任何位置时关闭菜单。监听body元素上的事件会使代码更加脆弱。像这样无辜的风格会破坏它:body{margin-left:auto;margin-right:auto;width:960px;}

使用流中断、模糊/聚焦事件或任何其他棘手的技术,只需将事件流与元素的亲属关系匹配即可:

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

要删除事件侦听器外部的单击,只需执行以下操作:

$(document).off("click.menu-outside");

我在YUI 3中是这样做的:

// Detect the click anywhere other than the 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();
    }
});

我正在检查祖先是否不是小部件元素容器,如果目标不是打开控件/元素的对象,如果我想关闭的小部件/元素已经打开(不那么重要)。

我只是想让@Pistos的回答更加明显,因为它隐藏在评论中。

这个解决方案非常适合我

var elementToToggle = $('.some-element');
$(document).click( function(event) {
  if( $(event.target).closest(elementToToggle).length === 0 ) {
    elementToToggle.hide();
  }
});

在CoffeeScript中:

elementToToggle = $('.some-element')
$(document).click (event) ->
  if $(event.target).closest(elementToToggle).length == 0
    elementToToggle.hide()