如何检测元素外部的单击?

Sergio del Amo 2367 javascript jquery

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

jQuery可以这样吗?

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

Eran Galperi.. 1763

注意:使用stopEventPropagation()是应该避免的,因为它破坏了DOM中的正常事件流.有关更多信息,请参阅此文章.考虑使用这种方法,而不是

将单击事件附加到关闭窗口的文档正文.将单独的单击事件附加到容器,该容器将停止传播到文档正文.

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

$('#menucontainer').click(function(event){
    event.stopPropagation();
});

  • 这打破了#menucontainer中包含的许多内容的标准行为,包括按钮和链接.我很惊讶这个答案如此受欢迎. (684认同)
  • Philip Walton非常清楚地解释了为什么这个答案不是最好的解决方案:http://css-tricks.com/dangers-stopping-event-propagation/ (139认同)
  • 我也很惊讶这个解决方案获得了如此多的选票.对于任何具有stopPropagation http://jsfiddle.net/Flandre/vaNFw/3/的外部元素,这将失败 (99认同)
  • 它非常美丽,但你应该使用`$('html').click()`不是身体.身体总是有其内容的高度.它没有很多内容或屏幕非常高,它只适用于身体填充的部分. (93认同)
  • 这不会破坏#menucontainer中任何内容的行为,因为它位于传播链的​​底部,对于其中的任何内容. (74认同)
  • 我更喜欢将文档绑定到click事件,然后在需要时取消绑定事件.它效率更高. (25认同)
  • 我正在低估这一点,因为它不是我们应该使用的解决方案.这很危险,因为它会产生深远的影响.正如汤姆在上面的评论中所指出的,有一篇关于css-tricks.org的非常深入的文章,关于为什么这个答案很好. (10认同)
  • 这个答案打破了许多标准行为.它应该降级! (8认同)
  • 该解决方案通常很糟糕并且存在维护危险.基本上只是一个有时会发生工作的黑客攻击. (7认同)
  • 呃,实际上我选择了Art的解决方案,因为当第一个菜单仍然打开时,当有人点击第二个菜单时,我不想停止传播. (6认同)
  • 我已经发布了一个替代解决方案,这并没有打破他的行为http://stackoverflow.com/questions/152975/how-to-detect-a-click-outside-an-element/3028037#3028037 (5认同)
  • @medo它应该是`$(document)`. (4认同)
  • 如果您使用此技术在页面上有多个菜单,则不起作用.单击任何一个菜单将停止关闭所有其他菜单. (4认同)
  • 此解决方案打破了我的代码的其他部分.对不起,但我必须向下投票.我也很惊讶这是如此受欢迎. (4认同)
  • http://stackoverflow.com/a/12920446/470159我已经回答了类似的问题,我认为这是一个很好的解决方案 (3认同)
  • 我不能说我很惊讶这个答案得到了如此多的赞成,它在许多情况下都有效.但这不是解决这个问题的最佳方案!就个人而言,我喜欢在函数范围内的函数内部进行处理:http://stackoverflow.com/questions/152975/how-to-detect-a-click-outside-an-element/3028037#3028037 http:// stackoverflow.com/a/7385673/986862 (3认同)
  • 这里有一个很好的解释为什么不使用`event.stopPropagation()`https://css-tricks.com/dangers-stopping-event-propagation/ (3认同)
  • 我认为这对页面性能有危害,详见本文:https://css-tricks.com/dangers-stopping-event-propagation/ (2认同)

Art.. 1326

您可以侦听单击事件document,然后通过使用确保#menucontainer不是祖先或单击元素的目标 .closest().

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

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

编辑 - 2017-06-23

如果您打算关闭菜单并想要停止侦听事件,也可以在事件监听器之后进行清理.此函数将仅清除新创建的侦听器,并保留其他任何单击侦听器document.使用ES2015语法:

export function hideOnClickOutside(selector) {
  const outsideClickListener = (event) => {
    $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注释!element.contains(event.target)而不是使用jQuery部分.

但是element.closest()现在所有主流浏览器都可以使用(W3C版本与jQuery版本略有不同).Polyfills可以在这里找到:https://developer.mozilla.org/en-US/docs/Web/API/Element/closest

  • 我实际上最终得到了这个解决方案,因为它更好地支持同一页面上的多个菜单,在第一个打开时点击第二个菜单将在stopPropagation解决方案中保留第一个菜单. (37认同)
  • 我尝试了很多其他的答案,但只有这个有效.谢谢.我最终使用的代码是:$(document).click(function(event){if($(event.target).closest('.window').length == 0){$('.window' ).fadeOut('fast');}}); (26认同)
  • **没有jQuery** - `!element.contains(event.target)`使用[Node.contains()](https://developer.mozilla.org/en-US/docs/Web/API/Node/contains ) (17认同)
  • 对于页面上的多个项目,这是一个非常好的解决方案. (13认同)
  • 很好的答案.当您有多个要关闭的项目时,这是要走的路. (13认同)
  • 这应该是公认的答案,因为其他解决方案与event.stopPropagation()存在缺陷. (4认同)
  • 这应该是公认的答案,更详细的答案请参考此页:http://css-tricks.com/dangers-stopping-event-propagation/ (4认同)
  • "`.closest()`方法在进行DOM树之前开始用元素本身进行搜索" - [api.jquery.com/closest](https://api.jquery.com/closest/).所以应该可以删除`&&!$(event.target).is('#menucontainer')`. (4认同)
  • 这是一个比使用$('html').stopPropagation()更好的解决方案,因为这些很可能会干扰网站其他部分的功能.我希望有人会将此标记为答案. (3认同)
  • 对于响应式设计而不是单击使用,如下所示:$(document).on('touchstart click',function(event){... (3认同)

zzzzBov.. 277

如何检测元素外的点击?

这个问题如此受欢迎并且答案如此之多的原因在于它看起来很复杂.经过近八年的时间和几十个答案,我真的很惊讶地看到对可访问性的关注度很低.

当用户点击菜单区域外时,我想隐藏这些元素.

这是一个崇高的事业,也是实际问题.问题的标题 - 大多数答案似乎试图解决 - 包含一个不幸的红鲱鱼.

提示:这是"点击"这个词!

您实际上并不想绑定点击处理程序.

如果您绑定了点击处理程序以关闭对话框,那么您已经失败了.你失败的原因是不是每个人都会触发click事件.不使用鼠标的用户将能够通过按下来转义对话框(并且您的弹出菜单可以说是一种对话框)Tab,然后他们将无法在不随后触发click事件的情况下读取对话框后面的内容.

所以让我们重新解释一下这个问题.

当用户完成对话时,如何关闭对话框?

这是目标.不幸的是,现在我们需要绑定userisfinishedwiththedialog事件,并且绑定不是那么简单.

那么我们如何检测用户是否已完成对话框的使用?

focusout 事件

一个好的开始是确定焦点是否已离开对话框.

提示:小心blur事件,blur如果事件被绑定到冒泡阶段,则不会传播!

jQuery focusout会做得很好.如果你不能使用jQuery,那么你可以blur在捕获阶段使用:

element.addEventListener('blur', ..., true);
//                       use capture: ^^^^

此外,对于许多对话框,您需要允许容器获得焦点.添加tabindex="-1"以允许对话框动态接收焦点,而不会中断选项卡流.

$('a').on('click', function () {
  $(this.hash).toggleClass('active').focus();
});

$('div').on('focusout', function () {
  $(this).removeClass('active');
});
div {
  display: none;
}
.active {
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#example">Example</a>
<div id="example" tabindex="-1">
  Lorem ipsum <a href="http://example.com">dolor</a> sit amet.
</div>


如果您使用该演示超过一分钟,您应该很快就会看到问题.

第一个是对话框中的链接不可点击.尝试单击它或选项卡到它将导致对话关闭在交互发生之前.这是因为聚焦内部元素会focusoutfocusin再次触发事件之前触发事件.

修复是在事件循环上对状态更改进行排队.这可以通过使用setImmediate(...)setTimeout(..., 0)不支持的浏览器来完成setImmediate.排队后,可以通过后续取消focusin:

$('.submenu').on({
  focusout: function (e) {
    $(this).data('submenuTimer', setTimeout(function () {
      $(this).removeClass('submenu--active');
    }.bind(this), 0));
  },
  focusin: function (e) {
    clearTimeout($(this).data('submenuTimer'));
  }
});

$('a').on('click', function () {
  $(this.hash).toggleClass('active').focus();
});

$('div').on({
  focusout: function () {
    $(this).data('timer', setTimeout(function () {
      $(this).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this).data('timer'));
  }
});
div {
  display: none;
}
.active {
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#example">Example</a>
<div id="example" tabindex="-1">
  Lorem ipsum <a href="http://example.com">dolor</a> sit amet.
</div>

第二个问题是再次按下链接时对话框不会关闭.这是因为对话框失去焦点,触发关闭行为,之后链接单击触发对话框重新打开.

与前一个问题类似,需要管理焦点状态.鉴于状态更改已经排队,只需在对话框触发器上处理焦点事件:

这应该看起来很熟悉
$('a').on({
  focusout: function () {
    $(this.hash).data('timer', setTimeout(function () {
      $(this.hash).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this.hash).data('timer'));  
  }
});

$('a').on('click', function () {
  $(this.hash).toggleClass('active').focus();
});

$('div').on({
  focusout: function () {
    $(this).data('timer', setTimeout(function () {
      $(this).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this).data('timer'));
  }
});

$('a').on({
  focusout: function () {
    $(this.hash).data('timer', setTimeout(function () {
      $(this.hash).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this.hash).data('timer'));  
  }
});
div {
  display: none;
}
.active {
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#example">Example</a>
<div id="example" tabindex="-1">
  Lorem ipsum <a href="http://example.com">dolor</a> sit amet.
</div>


Esc

如果您认为自己是通过处理焦点状态来完成的,那么您可以采取更多措施来简化用户体验.

这通常是一个"很好的"功能,但是当你有一个任何类型的模态或弹出Esc键时,键会关闭它是很常见的.

keydown: function (e) {
  if (e.which === 27) {
    $(this).removeClass('active');
    e.preventDefault();
  }
}

$('a').on('click', function () {
  $(this.hash).toggleClass('active').focus();
});

$('div').on({
  focusout: function () {
    $(this).data('timer', setTimeout(function () {
      $(this).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this).data('timer'));
  },
  keydown: function (e) {
    if (e.which === 27) {
      $(this).removeClass('active');
      e.preventDefault();
    }
  }
});

$('a').on({
  focusout: function () {
    $(this.hash).data('timer', setTimeout(function () {
      $(this.hash).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this.hash).data('timer'));  
  }
});
div {
  display: none;
}
.active {
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#example">Example</a>
<div id="example" tabindex="-1">
  Lorem ipsum <a href="http://example.com">dolor</a> sit amet.
</div>


如果您知道对话框中有可聚焦元素,则无需直接聚焦对话框.如果您正在构建菜单,则可以改为聚焦第一个菜单项.

click: function (e) {
  $(this.hash)
    .toggleClass('submenu--active')
    .find('a:first')
    .focus();
  e.preventDefault();
}

$('.menu__link').on({
  click: function (e) {
    $(this.hash)
      .toggleClass('submenu--active')
      .find('a:first')
      .focus();
    e.preventDefault();
  },
  focusout: function () {
    $(this.hash).data('submenuTimer', setTimeout(function () {
      $(this.hash).removeClass('submenu--active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this.hash).data('submenuTimer'));  
  }
});

$('.submenu').on({
  focusout: function () {
    $(this).data('submenuTimer', setTimeout(function () {
      $(this).removeClass('submenu--active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this).data('submenuTimer'));
  },
  keydown: function (e) {
    if (e.which === 27) {
      $(this).removeClass('submenu--active');
      e.preventDefault();
    }
  }
});
.menu {
  list-style: none;
  margin: 0;
  padding: 0;
}
.menu:after {
  clear: both;
  content: '';
  display: table;
}
.menu__item {
  float: left;
  position: relative;
}

.menu__link {
  background-color: lightblue;
  color: black;
  display: block;
  padding: 0.5em 1em;
  text-decoration: none;
}
.menu__link:hover,
.menu__link:focus {
  background-color: black;
  color: lightblue;
}

.submenu {
  border: 1px solid black;
  display: none;
  left: 0;
  list-style: none;
  margin: 0;
  padding: 0;
  position: absolute;
  top: 100%;
}
.submenu--active {
  display: block;
}

.submenu__item {
  width: 150px;
}

.submenu__link {
  background-color: lightblue;
  color: black;
  display: block;
  padding: 0.5em 1em;
  text-decoration: none;
}

.submenu__link:hover,
.submenu__link:focus {
  background-color: black;
  color: lightblue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="menu">
  <li class="menu__item">
    <a class="menu__link" href="#menu-1">Menu 1</a>
    <ul class="submenu" id="menu-1" tabindex="-1">
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#1">Example 1</a></li>
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#2">Example 2</a></li>
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#3">Example 3</a></li>
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#4">Example 4</a></li>
    </ul>
  </li>
  <li class="menu__item">
    <a  class="menu__link" href="#menu-2">Menu 2</a>
    <ul class="submenu" id="menu-2" tabindex="-1">
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#1">Example 1</a></li>
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#2">Example 2</a></li>
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#3">Example 3</a></li>
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#4">Example 4</a></li>
    </ul>
  </li>
</ul>
lorem ipsum <a href="http://example.com/">dolor</a> sit amet.


WAI-ARIA角色和其他辅助功能支持

这个答案有希望涵盖这个功能的可访问键盘和鼠标支持的基础知识,但由于它已经相当大,我将避免任何关于WAI-ARIA角色和属性的讨论,但我强烈建议实施者参考规范了解详情他们应该使用什么角色以及任何其他适当的属性.

  • 这是最完整的答案,考虑到解释和可访问性.我认为这应该是公认的答案,因为大多数其他答案只处理点击,只是代码片段丢弃没有任何解释. (26认同)
  • 虽然这是检测单击自定义下拉列表或其他输入的一种很好的方法,但它绝不应该是模式或弹出窗口的首选方法,原因有两个.当用户切换到另一个选项卡或窗口时,模态将关闭,或者打开上下文菜单,这实际上很烦人.此外,"click"事件会在鼠标向上时触发,而"focusout"事件会触发您按下鼠标的瞬间.通常情况下,如果按下鼠标按钮然后放开,按钮只会执行操作.为模态执行此操作的正确,可访问的方法是添加一个tabbable关闭按钮. (6认同)
  • 很棒,很好解释.我只是在React组件上使用了这种方法并且非常感谢 (4认同)
  • @zzzzBov感谢深刻的回答,我试图在vanilla JS中实现它,而且我对所有jquery的东西都有点失落.香草js有什么类似的东西吗? (2认同)
  • @zzzzBov不,我不是在找你写一个免费的jQuery版本,我会尝试这样做,我想最好是在这里问一个新问题,如果我真的卡住了.再次感谢. (2认同)

Dennis.. 139

这里的其他解决方案对我不起作用,所以我不得不使用:

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

  • 这对我有用,除了我在`IF`语句中添加`&&!$(event.target).parents("#foo").is("#foo")`以便任何子元素都不会关闭单击菜单. (42认同)
  • 处理深度嵌套的简洁改进是使用`.is('#foo,#foo*')`,但[我不建议使用绑定点击处理程序来解决此问题](http://stackoverflow.com/a /四十九万七千四百一十八分之三千八百三十一万七千七百六十八). (3认同)

Joe Lencioni.. 126

我有一个与Eran的例子类似的应用程序,除了我打开菜单时将click事件附加到正文...有点像这样:

$('#menucontainer').click(function(event) {
  $('html').one('click',function() {
    // Hide the menus
  });

  event.stopPropagation();
});

有关jQuery 功能的更多信息one()

  • 但是如果你单击菜单本身,那么外面,它将无法正常工作:) (9认同)
  • 这个问题是"one"适用于多次向数组添加事件的jQuery方法.因此,如果您单击菜单多次打开它,事件将再次绑定到正文并尝试多次隐藏菜单.应该使用故障保护来解决此问题. (4认同)
  • @Cody我认为没有帮助.`one`处理程序将自动调用`off`(如jQuery文档中所示). (4认同)
  • 在将click侦听器绑定到body之前放入event.stopProgagantion()会很有帮助. (3认同)

Rameez Rami.. 40

经过研究,我找到了三种工作方案(我忘了页面链接供参考)

第一解决方案

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

二解决方案

<script>
    $('body').on('click', function(e) {
        if($(e.target).closest('#testDiv').length == 0) {
           /* Hide dropdown here */
        }
    });
</script>

第三种方案

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

  • 第三种解决方案是迄今为止最优雅的检查方式.它也不涉及jQuery的任何开销.非常好.它帮助了很多.谢谢. (8认同)

小智.. 38

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

对我来说就好了.

  • @borrel blur*不是*移动到容器外面.Blur是焦点的反面,你正在考虑使用mouseout.当我创建"单击以编辑"文本时,此解决方案对我来说特别有效,我在点击时在纯文本和输入字段之间来回切换. (3认同)
  • 这是我使用的那个.它可能不是完美的,但作为一个业余爱好程序员,它很简单,可以清楚地理解. (2认同)

Wolfram.. 37

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

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

  • 该元素被添加到一个数组中,该数组包含clickoutside处理程序的所有元素
  • a(命名空间)单击处理程序绑定到文档(如果尚未存在)
  • 对于文档中的任何单击,触发clickoutside事件,以便该数组中那些不等于或者是click -events目标的父元素的元素
  • 另外,clickoutside事件的event.target 设置为用户点击的元素(这样你甚至可以知道用户点击了什么,而不仅仅是他点击了外面)

因此,没有事件停止传播,并且可以在具有外部处理程序的元素"上方"使用其他点击处理程序.


小智.. 31

这完全适合我!

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


34m0.. 25

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

上面没有找到满意的答案促使我前几天写这篇博文.对于更迂腐的人来说,有许多值得注意的问题:

  1. 如果在单击时将click事件处理程序附加到body元素,请确保在关闭菜单之前等待第二次单击,并取消绑定事件.否则,打开菜单的单击事件将冒泡到必须关闭菜单的侦听器.
  2. 如果对click事件使用event.stopPropogation(),则页面中没有其他元素可以具有click-anywhere-to-close功能.
  3. 将click事件处理程序无限期地附加到body元素不是一个高性能的解决方案
  4. 将事件的目标及其父项与处理程序的创建者进行比较假定您想要的是在单击它时关闭菜单,当您真正想要的是在单击页面上的任何位置时关闭它.
  5. 在body元素上侦听事件会使代码更加脆弱.造型像无辜一样会破坏它:body { margin-left:auto; margin-right: auto; width:960px;}


benb.. 24

正如另一张海报所说,有很多陷阱,特别是如果您正在显示的元素(在这种情况下是一个菜单)具有交互元素.我发现以下方法相当健壮:

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

    })
});


Jitendra Dam.. 23

这种情况的简单解决方案是:

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

上面的脚本将隐藏divif divclick事件的外部是否被触发.

您可以在以下博客中查看更多信息:http://www.codecanal.com/detect-click-outside-div-using-javascript/


Iman Sedighi.. 21

解决方法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
    }
});


Chris MacDon.. 20

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

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


Chu Yeow.. 17

我有这样的成功:

var $menuscontainer = ...;

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

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

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

逻辑是:当#menuscontainer显示时,将单击处理程序绑定到#menuscontainer仅在目标(单击)不是它的子项时隐藏的主体.


Bohdan Lyzan.. 16

作为变种:

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解决方案中保留第一个菜单.


nazar kuliye.. 13

我在一些jQuery日历插件中找到了这个方法.

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


Dan Philip.. 13

该事件有一个名为event.path的属性,该属性是"树中所有祖先的静态有序列表".要检查事件是源自特定DOM元素还是其中一个子元素,只需检查该特定DOM元素的路径.它还可以用于通过逻辑OR检查some函数中的元素检查来检查多个元素.

$("body").click(function() {
  target = document.getElementById("main");
  flag = event.path.some(function(el, i, arr) {
    return (el == target)
  })
  if (flag) {
    console.log("Inside")
  } else {
    console.log("Outside")
  }
});
#main {
  display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="main">
  <ul>
    <li>Test-Main</li>
    <li>Test-Main</li>
    <li>Test-Main</li>
    <li>Test-Main</li>
    <li>Test-Main</li>
  </ul>
</div>
<div id="main2">
  Outside Main
</div>

所以对于你的情况它应该是

$("body").click(function() {
  target = $("#menuscontainer")[0];
  flag = event.path.some(function(el, i, arr) {
    return (el == target)
  });
  if (!flag) {
    // Hide the menus
  }
});


小智.. 12

这是针对未来观众的vanilla JavaScript解决方案.

单击文档中的任何元素后,如果切换了单击元素的id,或者未隐藏隐藏元素且隐藏元素不包含单击元素,则切换元素.

(function () {
    "use strict";
    var hidden = document.getElementById('hidden');
    document.addEventListener('click', function (e) {
        if (e.target.id == 'toggle' || (hidden.style.display != 'none' && !hidden.contains(e.target))) hidden.style.display = hidden.style.display == 'none' ? 'block' : 'none';
    }, false);
})();

(function () {
    "use strict";
    var hidden = document.getElementById('hidden');
    document.addEventListener('click', function (e) {
        if (e.target.id == 'toggle' || (hidden.style.display != 'none' && !hidden.contains(e.target))) hidden.style.display = hidden.style.display == 'none' ? 'block' : 'none';
    }, false);
})();
<a href="javascript:void(0)" id="toggle">Toggle Hidden Div</a>
<div id="hidden" style="display: none;">This content is normally hidden. click anywhere other than this content to make me disappear</div>

如果您要在同一页面上进行多次切换,可以使用以下内容:

  1. 将类名添加hidden到可折叠项.
  2. 单击文档后,关闭所有不包含单击元素且未隐藏的隐藏元素
  3. 如果单击的元素是切换,则切换指定的元素.

(function () {
    "use strict";
    var hiddenItems = document.getElementsByClassName('hidden'), hidden;
    document.addEventListener('click', function (e) {
        for (var i = 0; hidden = hiddenItems[i]; i++) {
            if (!hidden.contains(e.target) && hidden.style.display != 'none')
                hidden.style.display = 'none';
        }
        if (e.target.getAttribute('data-toggle')) {
            var toggle = document.querySelector(e.target.getAttribute('data-toggle'));
            toggle.style.display = toggle.style.display == 'none' ? 'block' : 'none';
        }
    }, false);
})();
<a href="javascript:void(0)" data-toggle="#hidden1">Toggle Hidden Div</a>
<div class="hidden" id="hidden1" style="display: none;" data-hidden="true">This content is normally hidden</div>
<a href="javascript:void(0)" data-toggle="#hidden2">Toggle Hidden Div</a>
<div class="hidden" id="hidden2" style="display: none;" data-hidden="true">This content is normally hidden</div>
<a href="javascript:void(0)" data-toggle="#hidden3">Toggle Hidden Div</a>
<div class="hidden" id="hidden3" style="display: none;" data-hidden="true">This content is normally hidden</div>


Jovanni G.. 11

我很惊讶没有人真正认可的focusout事件:

var button = document.getElementById('button');
button.addEventListener('click', function(e){
  e.target.style.backgroundColor = 'green';
});
button.addEventListener('focusout', function(e){
  e.target.style.backgroundColor = '';
});
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
</head>
<body>
  <button id="button">Click</button>
</body>
</html>


小智.. 8

如果你是IE和FF 3.*的脚本,你只想知道某个框区域内是否发生了点击,你也可以使用类似的东西:

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


webenformasy.. 8

使用:

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


mems.. 8

相反,使用流动中断,模糊/焦点事件或任何其他棘手的技术,只需将事件流与元素的亲缘关系匹配:

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


Salman A.. 6

在文档上挂钩单击事件侦听器.在事件侦听器中,您可以查看事件对象,特别是event.target,以查看单击了哪个元素:

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


小智.. 5

$(document).click(function() {
    $(".overlay-window").hide();
});
$(".overlay-window").click(function() {
    return false;
});

如果单击文档,则隐藏给定元素,除非您单击该元素.


Satya Prakas.. 5

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

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


bbe.. 5

Upvote为最受欢迎的答案,但添加

&& (e.target != $('html').get(0)) // ignore the scrollbar

因此,单击滚动条不会[隐藏或不管]您的目标元素.


Matt Goodwin.. 5

为了更容易使用,以及更具表现力的代码,我为此创建了一个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;
};

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

使用如下:

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


小智.. 5

如果有人好奇这里是javascript解决方案(es6):

window.addEventListener('mouseup', e => {
        if (e.target != yourDiv && e.target.parentNode != yourDiv) {
            yourDiv.classList.remove('show-menu');
            //or yourDiv.style.display = 'none';
        }
    })

和es5,以防万一:

window.addEventListener('mouseup', function (e) {
if (e.target != yourDiv && e.target.parentNode != yourDiv) {
    yourDiv.classList.remove('show-menu'); 
    //or yourDiv.style.display = 'none';
}

});


Duannx.. 5

这是纯JavaScript的简单解决方案。ES6最新的

var isMenuClick = false;
var menu = document.getElementById('menuscontainer');
document.addEventListener('click',()=>{
    if(!isMenuClick){
       //Hide the menu here
    }
    //Reset isMenuClick 
    isMenuClick = false;
})
menu.addEventListener('click',()=>{
    isMenuClick = true;
})


Rinto George.. 5

我已经使用下面的脚本并完成了jQuery。

jQuery(document).click(function(e) {
    var target = e.target; //target div recorded
    if (!jQuery(target).is('#tobehide') ) {
        jQuery(this).fadeOut(); //if the click element is not the above id will hide
    }
})

在下面找到HTML代码

<div class="main-container">
<div> Hello I am the title</div>
<div class="tobehide">I will hide when you click outside of me</div>
</div>

您可以在这里阅读教程


归档时间:

查看次数:

1161863 次

最近记录:

10 月,3 周 前