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

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

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

当前回答

作为一种变体:

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解决方案中的第一个菜单保持打开状态。

其他回答

这是我解决问题的方法。

$(window).click(function (event) {
    //To improve performance add a checklike 
    //if(myElement.isClosed) return;
    var isClickedElementChildOfMyBox = isChildOfElement(event,'#id-of-my-element');

    if (isClickedElementChildOfMyBox)
        return;

    //your code to hide the element 
});

var isChildOfElement = function (event, selector) {
    if (event.originalEvent.path) {
        return event.originalEvent.path[0].closest(selector) !== null;
    }

    return event.originalEvent.originalTarget.closest(selector) !== null;
}

尝试以下代码:

if ($(event.target).parents().index($('#searchFormEdit')) == -1 &&
    $(event.target).parents().index($('.DynarchCalendar-topCont')) == -1 &&
    (_x < os.left || _x > (os.left + 570) || _y < os.top || _y > (os.top + 155)) &&
    isShowEditForm) {

    setVisibleEditForm(false);
}

您可以监听文档上的单击事件,然后使用.clost()确保#menucontainer不是被单击元素的祖先或目标。

如果不是,则单击的元素位于#menucontainer之外,您可以安全地隐藏它。

$(document).click(function(event) { 
  var $target = $(event.target);
  if(!$target.closest('#menucontainer').length && 
  $('#menucontainer').is(":visible")) {
    $('#menucontainer').hide();
  }        
});

编辑–2017-06-23

如果您计划关闭菜单并希望停止侦听事件,也可以在事件侦听器之后进行清理。此函数将只清理新创建的侦听器,保留文档上的任何其他单击侦听器。使用ES2015语法:

export function hideOnClickOutside(selector) {
  const outsideClickListener = (event) => {
    const $target = $(event.target);
    if (!$target.closest(selector).length && $(selector).is(':visible')) {
        $(selector).hide();
        removeClickListener();
    }
  }

  const removeClickListener = () => {
    document.removeEventListener('click', outsideClickListener);
  }

  document.addEventListener('click', outsideClickListener);
}

编辑–2018-03-11

对于那些不想使用jQuery的人。这是上面的纯vanillaJS(ECMAScript6)代码。

function hideOnClickOutside(element) {
    const outsideClickListener = event => {
        if (!element.contains(event.target) && isVisible(element)) { // or use: event.target.closest(selector) === null
          element.style.display = 'none';
          removeClickListener();
        }
    }

    const removeClickListener = () => {
        document.removeEventListener('click', outsideClickListener);
    }

    document.addEventListener('click', outsideClickListener);
}

const isVisible = elem => !!elem && !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); // source (2018-03-11): https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js 

注:这是基于Alex的评论,仅供使用!元素。包含(event.target)而不是jQuery部分。

但是element.clost()现在也可以在所有主要浏览器中使用(W3C版本与jQuery版本稍有不同)。Polyfills可以在以下位置找到:Element.clost()

编辑–2020-05-21

如果您希望用户能够在元素内部单击并拖动,则在元素外部释放鼠标,而不关闭元素:

      ...
      let lastMouseDownX = 0;
      let lastMouseDownY = 0;
      let lastMouseDownWasOutside = false;

      const mouseDownListener = (event: MouseEvent) => {
        lastMouseDownX = event.offsetX;
        lastMouseDownY = event.offsetY;
        lastMouseDownWasOutside = !$(event.target).closest(element).length;
      }
      document.addEventListener('mousedown', mouseDownListener);

在外部ClickListener中:

const outsideClickListener = event => {
        const deltaX = event.offsetX - lastMouseDownX;
        const deltaY = event.offsetY - lastMouseDownY;
        const distSq = (deltaX * deltaX) + (deltaY * deltaY);
        const isDrag = distSq > 3;
        const isDragException = isDrag && !lastMouseDownWasOutside;

        if (!element.contains(event.target) && isVisible(element) && !isDragException) { // or use: event.target.closest(selector) === null
          element.style.display = 'none';
          removeClickListener();
          document.removeEventListener('mousedown', mouseDownListener); // Or add this line to removeClickListener()
        }
    }
 <div class="feedbackCont" onblur="hidefeedback();">
        <div class="feedbackb" onclick="showfeedback();" ></div>
        <div class="feedbackhide" tabindex="1"> </div>
 </div>

function hidefeedback(){
    $j(".feedbackhide").hide();
}

function showfeedback(){
    $j(".feedbackhide").show();
    $j(".feedbackCont").attr("tabindex",1).focus();
}

这是我想出的最简单的解决方案。

现在有一个插件:外部事件(博客文章)

当clickoutside处理程序(WLOG)绑定到元素时,会发生以下情况:

元素被添加到一个数组中,该数组包含所有具有clickoutside处理程序的元素(名称空间)单击处理程序绑定到文档(如果尚未存在)在文档中的任何单击时,都会为该数组中不等于或不是单击事件目标的父级的元素触发单击外部事件此外,clickoutside事件的event.target设置为用户单击的元素(因此您甚至可以知道用户单击了什么,而不仅仅是他在外面单击了什么)

因此,不会阻止任何事件的传播,并且可以在元素的“上方”使用额外的单击处理程序和外部处理程序。