$(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();
}
});
// HIDE SEARCH BOX IF CLICKING OUTSIDE
$(document).click(function(event){
// IF NOT CLICKING THE SEARCH BOX OR ITS CONTENTS OR SEARCH ICON
if ($("#search-holder").is(":visible") && !$(event.target).is("#search-holder *, #search")) {
$("#search-holder").fadeOut('fast');
$("#search").removeClass('active');
}
});
它首先检查搜索框是否已经可见,在我们的例子中,它还删除了隐藏/显示搜索按钮上的活动类 .
8
作为变种:
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();
}
});
这个答案有希望涵盖这个功能的可访问键盘和鼠标支持的基础知识,但因为它会避免讨论WAI-ARIA roles and attributes,但是我强烈建议实施者参考规范,了解他们应该使用什么角色以及任何其他角色的详细信息 . 适当的属性 .
121
经过研究,我找到了三个有效的解决方案(我忘了页面链接供参考)
第一个解决方案
<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>
$('#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);
}
})
});
而不是使用可能有一些副作用的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
}
});
<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>
如果您要在同一页面上进行多次切换,可以使用以下内容:
将类名 hidden 添加到可折叠项目 .
单击文档后,关闭所有不包含单击元素且未隐藏的隐藏元素
如果单击的元素是切换,则切换指定的元素 .
(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);
})();
$(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");
13
给予好评对于最流行的答案,但添加
&& (e.target != $('html').get(0)) // ignore the scrollbar
$(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");
}
});
该事件有一个名为event.path的属性,该元素是"static ordered list of all its ancestors in tree order" . 要检查事件是源自特定DOM元素还是其中一个子元素,只需检查该特定DOM元素的路径 . 它还可以用于通过 some 函数中的元素检查逻辑 OR 来检查多个元素 .
$("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")
}
});
30 回答
我成功了这样的事情:
逻辑是:当显示
#menuscontainer
时,仅当目标(点击)不是它的子项时,将单击处理程序绑定到隐藏#menuscontainer
的主体 .这种情况的简单解决方案是:
如果触发
div
click事件之外,上面的脚本将隐藏div
.您可以在以下博客中查看更多信息:http://www.codecanal.com/detect-click-outside-div-using-javascript/
这里的其他解决方案对我不起作用所以我不得不使用:
我们实施了一个解决方案,部分基于上述用户的评论,这对我们来说非常有效 . 我们使用它来隐藏搜索框/结果,当在这些元素外部单击时,不包括最初的元素 .
它首先检查搜索框是否已经可见,在我们的例子中,它还删除了隐藏/显示搜索按钮上的活动类 .
作为变种:
stopping event propagation没有问题,并且更好地支持同一页面上的多个菜单,在第一个打开时单击第二个菜单将在stopPropagation解决方案中保留第一个菜单 .
我在一些jQuery日历插件中找到了这个方法 .
这个问题如此受欢迎并且答案如此之多的原因在于它看起来很复杂 . 经过近八年的时间和几十个答案,我真的很惊讶地看到对可访问性的关注度很低 .
这是一个崇高的事业,也是实际问题 . 问题的 Headers - 这是大多数答案似乎试图解决的问题 - 包含一个不幸的红鲱鱼 .
Hint: it's the word "click"!
您实际上并不想绑定点击处理程序 .
如果你_124995已经失败了 . 你失败的原因是不是每个人都会触发
click
事件 . 不使用鼠标的用户可以通过按Tab键来转义对话框(并且您的弹出菜单可以说是一种对话框),然后他们将无法在不随后触发click
的情况下读取对话框后面的内容事件 .让我们重新解释一下这个问题 .
这是目标 . 不幸的是,现在我们需要绑定
userisfinishedwiththedialog
事件,并且绑定不是那么简单 .那么我们怎样才能检测到用户已经完成了对话框的使用?
焦点事件
一个好的开始是确定焦点是否已离开对话框 .
Hint: be careful with the blur event, blur doesn't propagate if the event was bound to the bubbling phase!
jQuery的focusout会做得很好 . 如果你不能使用jQuery,那么你可以在捕获阶段使用
blur
:此外,对于许多对话框,您需要允许容器获得焦点 . 添加
tabindex="-1"
以允许对话框动态接收焦点,而不会中断标签流 .如果您使用该演示超过一分钟,您应该很快就会看到问题 .
第一个是对话框中的链接不可点击 . 尝试单击它或选项卡到它将导致对话关闭在交互发生之前 . 这是因为聚焦内部元素会在再次触发
focusin
事件之前触发focusout
事件 .修复是在事件循环上对状态更改进行排队 . 对于不支持
setImmediate
的浏览器,可以使用setImmediate(...)
或setTimeout(..., 0)
来完成此操作 . 排队后,可以通过后续的focusin
取消:第二个问题是再次按下链接时对话框不会关闭 . 这是因为对话框失去焦点,触发关闭行为,之后链接单击触发对话框重新打开 .
与前一个问题类似,需要管理焦点状态 . 鉴于状态更改已经排队,只需在对话框触发器上处理焦点事件:
这应该看起来很熟悉
Esc键
如果您认为自己是通过处理焦点状态来完成的,那么您可以采取更多措施来简化用户体验 .
这通常是"nice to have"功能,但是当你有任何类型的模态或弹出窗口时,Esc键会关闭它 .
如果您知道对话框中有可聚焦元素,则无需直接对焦对话框 . 如果您正在构建菜单,则可以改为聚焦第一个菜单项 .
WAI-ARIA角色和其他辅助功能支持
这个答案有希望涵盖这个功能的可访问键盘和鼠标支持的基础知识,但因为它会避免讨论WAI-ARIA roles and attributes,但是我强烈建议实施者参考规范,了解他们应该使用什么角色以及任何其他角色的详细信息 . 适当的属性 .
经过研究,我找到了三个有效的解决方案(我忘了页面链接供参考)
第一个解决方案
第二个解决方案
第三种解决方案
功能:
用法:
功能很简单:
使用:
正如另一张海报所说,有很多陷阱,特别是如果您正在显示的元素(在这种情况下是菜单)具有交互元素 . 我发现以下方法相当健壮:
如果您正在编写IE和FF 3. *的脚本,并且您只想知道某个框区域内是否发生了单击,您还可以使用以下内容:
这完全适合我!
解决方案1
而不是使用可能有一些副作用的event.stopPropagation(),只需定义一个简单的标志变量并添加一个
if
条件 . 我测试了这个并正常工作,没有任何副作用的stopPropagation:解决方案2
只需一个简单的
if
条件:我不认为你真正需要的是当用户点击外面时关闭菜单;您需要的是当用户点击页面上的任何位置时关闭菜单 . 如果您单击菜单,或关闭菜单,它应该关闭吗?
上面没有找到满意的答案促使我前几天写了this blog post . 对于更迂腐的人来说,有许多值得注意的问题:
如果在单击时将单击事件处理程序附加到body元素,请确保在关闭菜单之前等待第二次单击,并取消绑定事件 . 否则,打开菜单的click事件将冒泡到必须关闭菜单的侦听器 .
如果对click事件使用event.stopPropogation(),则页面中没有其他元素可以具有click-anywhere-to-close功能 .
无限期地将click事件处理程序附加到body元素不是一个高性能的解决方案
将事件的目标及其父项与处理程序的创建者进行比较假定您单击它时关闭菜单,当您单击页面上的任何位置时,您真正想要的是关闭它 .
在body元素上侦听事件会使代码更加脆弱 . 造型像无辜一样会破坏它:
body { margin-left:auto; margin-right: auto; width:960px;}
检查窗口单击事件目标(它应传播到窗口,只要它没有在其他任何地方捕获),并确保它不是任何菜单元素 . 如果不是,那么你就在菜单之外了 .
或者检查点击的位置,看看它是否包含在菜单区域中 .
如果单击文档,则隐藏给定元素,除非单击该元素 .
这是我解决这个问题的方法:
您可以在
document
上侦听 click 事件,然后使用.closest()确保#menucontainer
不是祖先或被点击元素的目标 .如果不是,则单击的元素位于
#menucontainer
之外,您可以安全地隐藏它 .编辑 - 2017-06-23
如果您打算关闭菜单并想要停止侦听事件,也可以在事件监听器之后进行清理 . 此函数将仅清除新创建的侦听器,并保留
document
上的任何其他单击侦听器 . 使用ES2015语法:编辑 - 2018-03-11
对于那些不想使用jQuery的人 . 这是普通vanillaJS(ECMAScript6)中的上述代码 .
NOTE: 这是基于Alex评论只使用
!element.contains(event.target)
而不是jQuery部分 .但
element.closest()
现在也可用于所有主流浏览器(W3C版本与jQuery版本略有不同) . Polyfills可以在这里找到:https://developer.mozilla.org/en-US/docs/Web/API/Element/closest这是针对未来 Spectator 的vanilla JavaScript解决方案 .
单击文档中的任何元素后,如果切换了单击元素的id,或者未隐藏隐藏元素且隐藏元素不包含单击元素,则切换元素 .
如果您要在同一页面上进行多次切换,可以使用以下内容:
将类名
hidden
添加到可折叠项目 .单击文档后,关闭所有不包含单击元素且未隐藏的隐藏元素
如果单击的元素是切换,则切换指定的元素 .
对我来说就好了 .
相反,使用流动中断,模糊/焦点事件或任何其他棘手的技术,只需将事件流与元素的亲缘关系匹配:
要删除单击外部事件侦听器,只需:
给予好评对于最流行的答案,但添加
因此,单击滚动条不会[隐藏或任何]目标元素 .
将单击事件附加到关闭窗口的文档正文 . 将单独的单击事件附加到容器,该容器将停止传播到文档正文 .
在文档上挂钩单击事件侦听器 . 在事件监听器中,您可以查看event object,特别是event.target,以查看单击的元素:
现在有一个插件:outside events(blog post)
将clickoutside处理程序(WLOG)绑定到元素时会发生以下情况:
该元素被添加到一个数组中,该数组包含具有clickoutside处理程序的所有元素
a(namespaced)单击处理程序绑定到文档(如果尚未存在)
对文档中的任何单击,将触发该数组中不等于或点击事件目标的父元素的那些元素的clickoutside事件
另外,clickoutside事件的event.target设置为用户点击的元素(这样你甚至可以知道用户点击了什么,而不仅仅是他点击了外面)
因此,没有事件停止传播,并且可以使用其他单击处理程序"above"具有外部处理程序的元素 .
我最终做了这样的事情:
我在新容器中有一个关闭按钮,用于最终用户友好的UI用途 . 我不得不使用return false以便不通过 . 当然,在那里有一个A HREF带你到某个地方会很好,或者你可以调用一些ajax的东西 . 无论哪种方式,它对我都有效 . 正是我想要的 .
该事件有一个名为event.path的属性,该元素是"static ordered list of all its ancestors in tree order" . 要检查事件是源自特定DOM元素还是其中一个子元素,只需检查该特定DOM元素的路径 . 它还可以用于通过
some
函数中的元素检查逻辑OR
来检查多个元素 .所以对于你的情况它应该是
为了更容易使用,以及更具表现力的代码,我为此创建了一个jQuery插件:
注意: target 是用户实际点击的元素 . 但是回调仍然在原始元素的上下文中执行,因此您可以像在jQuery回调中所期望的那样利用 this .
插入:
默认情况下,单击事件侦听器放置在文档上 . 但是,如果要限制事件侦听器作用域,则可以传入表示父级别元素的jQuery对象,该父级别元素将是将要侦听单击的顶级父级 . 这可以防止不必要的文档级事件侦听器 . 显然,除非提供的父元素是初始元素的父元素,否则它将不起作用 .
使用如下:
我有一个类似于Eran示例的应用程序,除了我在打开菜单时将click事件附加到正文...有点像这样:
有关jQuery's one() function的更多信息