我有一些 HTML 菜单,当用户单击这些菜单的头部时,我会完全显示这些菜单。当用户在菜单区域之外单击时,我想隐藏这些元素。
jQuery可以做到这样的事情吗?
$("#menuscontainer").clickOutsideThisElement(function() {
// Hide the menus
});
我有一些 HTML 菜单,当用户单击这些菜单的头部时,我会完全显示这些菜单。当用户在菜单区域之外单击时,我想隐藏这些元素。
jQuery可以做到这样的事情吗?
$("#menuscontainer").clickOutsideThisElement(function() {
// Hide the menus
});
注意:使用
stopPropagation
是应该避免的,因为它会破坏 DOM 中的正常事件流。有关更多信息,请参阅这篇 CSS 技巧文章。请考虑改用此方法。
将单击事件附加到关闭窗口的文档正文。将单独的单击事件附加到容器,以停止传播到文档正文。
$(window).click(function() {
//Hide the menus if visible
});
$('#menucontainer').click(function(event){
event.stopPropagation();
});
您可以侦听单击事件document
,然后使用 确保#menucontainer
不是被单击元素的祖先或目标 .closest()
。
如果不是,则单击的元素在外部,#menucontainer
您可以安全地隐藏它。
$(document).click(function(event) {
var $target = $(event.target);
if(!$target.closest('#menucontainer').length &&
$('#menucontainer').is(":visible")) {
$('#menucontainer').hide();
}
});
如果您打算关闭菜单并希望停止侦听事件,您也可以在事件侦听器之后进行清理。此函数将仅清理新创建的侦听器,保留任何其他点击侦听器document
。使用 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)
}
对于那些不想使用 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 版本略有不同)。Polyfill 可以在这里找到:Element.closest()
如果您希望用户能够在元素内部单击并拖动,然后在元素外部释放鼠标,而不关闭元素:
...
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);
并在outsideClickListener
:
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()
}
}
如何检测元素外的点击?
这个问题如此受欢迎并且有这么多答案的原因是它看起来很复杂。经过近八年和几十个答案,我真的很惊讶看到对可访问性的关注如此之少。
当用户在菜单区域之外单击时,我想隐藏这些元素。
这是一个崇高的事业,也是真正的问题。问题的标题——这是大多数答案似乎试图解决的问题——包含一个不幸的红鲱鱼。
提示:是“点击”这个词!
如果您正在绑定单击处理程序以关闭对话框,那么您已经失败了。你失败的原因是不是每个人都会触发click
事件。不使用鼠标的用户将能够通过按 退出您的对话框(并且您的弹出菜单可以说是一种对话框)Tab,然后他们将无法在不触发click
事件的情况下阅读对话框后面的内容。
所以让我们重新表述这个问题。
当用户完成对话框时如何关闭它?
这是目标。不幸的是,现在我们需要绑定userisfinishedwiththedialog
事件,而绑定并不是那么简单。
那么我们如何才能检测到用户已经使用完一个对话框呢?
focusout
事件一个好的开始是确定焦点是否离开了对话框。
提示:小心blur
事件,blur
如果事件绑定到冒泡阶段,则不会传播!
jQueryfocusout
会做得很好。如果你不能使用 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>
如果您使用该演示超过一分钟,您应该很快就会开始看到问题。
首先是对话框中的链接不可点击。尝试单击它或使用选项卡将导致对话框在交互发生之前关闭。这是因为聚焦内部元素会在再次触发focusout
事件之前触发focusin
事件。
解决方法是在事件循环中对状态更改进行排队。这可以通过使用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键会将其关闭是很常见的。
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 角色和属性,但是我强烈建议实施者参考规范以获取详细信息他们应该使用什么角色以及任何其他适当的属性。
这里的其他解决方案对我不起作用,所以我不得不使用:
if(!$(event.target).is('#foo'))
{
// hide menu
}
我使用这种方法来处理在单击外部时关闭下拉菜单。
首先,我为组件的所有元素创建了一个自定义类名。此类名称将添加到构成菜单小部件的所有元素中。
const className = `dropdown-${Date.now()}-${Math.random() * 100}`;
我创建了一个函数来检查点击和被点击元素的类名。如果单击的元素不包含我上面生成的自定义类名,它应该将show
标志设置为false
并且菜单将关闭。
const onClickOutside = (e) => {
if (!e.target.className.includes(className)) {
show = false;
}
};
然后我将点击处理程序附加到窗口对象。
// add when widget loads
window.addEventListener("click", onClickOutside);
...最后是一些家政服务
// remove listener when destroying the widget
window.removeEventListener("click", onClickOutside);
我有一个与 Eran 的示例类似的应用程序,除了我在打开菜单时将点击事件附加到正文......有点像这样:
$('#menucontainer').click(function(event) {
$('html').one('click',function() {
// Hide the menus
});
event.stopPropagation();
});
更多关于jQueryone()
函数的信息
event.composedPath()
来自:https ://developer.mozilla.org/en-US/docs/Web/API/Event/composedPath
Event 接口的composedPath() 方法返回事件的路径,该路径是一个对象数组,将在其上调用侦听器。
const target = document.querySelector('#myTarget')
document.addEventListener('click', (event) => {
const withinBoundaries = event.composedPath().includes(target)
if (withinBoundaries) {
target.innerText = 'Click happened inside element'
} else {
target.innerText = 'Click happened **OUTSIDE** element'
}
})
/* just to make it good looking. you don't need this */
#myTarget {
margin: 50px auto;
width: 500px;
height: 500px;
background: gray;
border: 10px solid black;
}
<div id="myTarget">
click me (or not!)
</div>
经过研究,我找到了三个可行的解决方案(我忘记了参考的页面链接)
<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>
$("#menuscontainer").click(function() {
$(this).focus();
});
$("#menuscontainer").blur(function(){
$(this).hide();
});
对我有用。
当clickoutside处理程序 (WLOG) 绑定到元素时会发生以下情况:
因此,不会停止传播任何事件,并且可以在带有外部处理程序的元素“上方”使用额外的单击处理程序。
这对我来说非常有效!
$('html').click(function (e) {
if (e.target.id == 'YOUR-DIV-ID') {
//do something
} else {
//do something
}
});
这种情况的一个简单解决方案是:
$(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();
}
});
上面的脚本将隐藏div
如果div
点击事件之外的触发。
您可以查看以下博客了解更多信息: http: //www.codecanal.com/detect-click-outside-div-using-javascript/
我认为您真正需要的不是当用户在外面点击时关闭菜单;您需要的是当用户单击页面上的任何位置时关闭菜单。如果您单击菜单,或关闭菜单,它应该关闭吗?
在上面找不到令人满意的答案促使我前几天写了这篇博文。对于更迂腐的人,有一些问题需要注意:
body { margin-left:auto; margin-right: auto; width:960px;}
正如另一位海报所说,有很多陷阱,特别是如果您正在显示的元素(在本例中为菜单)具有交互元素。我发现以下方法相当稳健:
$('#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";
}
});
只需一个简单的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
}
});
我很惊讶没有人真正承认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>
我在这样的事情上取得了成功:
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
该事件有一个名为 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;
background:yellow;
}
<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
}
});
作为变体:
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 日历插件中找到了这个方法。
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);
这是面向未来观众的 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>
如果您要在同一页面上进行多个切换,则可以使用以下内容:
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);
})();
<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>
focusout
可访问性这里有一个答案说(非常正确)关注click
事件是一个可访问性问题,因为我们想要满足键盘用户的需求。该focusout
事件是在这里使用的正确方法,但它可以比其他答案更简单(也可以在纯 javascript 中):
使用的“问题”focusout
是,如果对话框/模态/菜单中的元素失去焦点,那么事件仍然会被触发。我们可以通过查看event.relatedTarget
(它告诉我们哪个元素将获得焦点)来检查情况是否如此。
dialog = document.getElementById("dialogElement")
dialog.addEventListener("focusout", function (event) {
if (
// we are still inside the dialog so don't close
dialog.contains(event.relatedTarget) ||
// we have switched to another tab so probably don't want to close
!document.hasFocus()
) {
return;
}
dialog.close(); // or whatever logic you want to use to close
});
上面有一个小问题,那就是relatedTarget
可能null
。如果用户在对话框外部单击,这很好,但如果除非用户在对话框内部单击并且对话框碰巧不可聚焦,那么这将是一个问题。要解决此问题,您必须确保设置tabIndex=0
为可聚焦对话框。
如果您正在为 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;}
采用:
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();
});
而不是使用流中断、模糊/焦点事件或任何其他棘手的技术,只需将事件流与元素的亲属关系匹配:
$(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");
如果有人在这里好奇是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';
}
});
这是纯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;
})
我使用了下面的脚本并使用 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>
你可以在这里阅读教程
2020 年使用原生 JS API最接近方法的解决方案。
document.addEventListener('click', ({ target }) => {
if (!target.closest('.el1, .el2, #el3')) {
alert('click outside')
}
})
$(document).click(function() {
$(".overlay-window").hide();
});
$(".overlay-window").click(function() {
return false;
});
如果单击文档,则隐藏给定元素,除非单击同一元素。
在文档上挂钩一个单击事件侦听器。在事件侦听器中,您可以查看事件对象,特别是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");
}
});
为最受欢迎的答案投票,但添加
&& (e.target != $('html').get(0)) // ignore the scrollbar
因此,单击滚动条不会[隐藏或其他]您的目标元素。
我在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();
}
});
我正在检查祖先是否不是小部件元素容器,
如果目标不是打开小部件/元素,
我要关闭的小部件/元素是否已经打开(不是那么重要)。
我们实施了一个解决方案,部分基于上述用户的评论,这对我们来说非常有效。当在这些元素之外单击时,我们使用它来隐藏搜索框/结果,不包括最初的元素。
// 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');
}
});
它首先检查搜索框是否已经可见,在我们的例子中,它还删除了隐藏/显示搜索按钮上的活动类。
为了更易于使用和更具表现力的代码,我为此创建了一个 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...
});
功能:
$(function() {
$.fn.click_inout = function(clickin_handler, clickout_handler) {
var item = this;
var is_me = false;
item.click(function(event) {
clickin_handler(event);
is_me = true;
});
$(document).click(function(event) {
if (is_me) {
is_me = false;
} else {
clickout_handler(event);
}
});
return this;
}
});
用法:
this.input = $('<input>')
.click_inout(
function(event) { me.ShowTree(event); },
function() { me.Hide(); }
)
.appendTo(this.node);
而且功能非常简单:
ShowTree: function(event) {
this.data_span.show();
}
Hide: function() {
this.data_span.hide();
}
这是我对这个问题的解决方案:
$(document).ready(function() {
$('#user-toggle').click(function(e) {
$('#user-nav').toggle();
e.stopPropagation();
});
$('body').click(function() {
$('#user-nav').hide();
});
$('#user-nav').click(function(e){
e.stopPropagation();
});
});
这应该有效:
$('body').click(function (event) {
var obj = $(event.target);
obj = obj['context']; // context : clicked element inside body
if ($(obj).attr('id') != "menuscontainer" && $('#menuscontainer').is(':visible') == true) {
//hide menu
}
});
当只管理一个元素时,这里的解决方案可以正常工作。但是,如果有多个元素,问题就会复杂得多。使用 e.stopPropagation() 和其他所有技巧都行不通。
我想出了一个解决方案,也许不是那么容易,但总比没有好。看一看:
$view.on("click", function(e) {
if(model.isActivated()) return;
var watchUnclick = function() {
rootView.one("mouseleave", function() {
$(document).one("click", function() {
model.deactivate();
});
rootView.one("mouseenter", function() {
watchUnclick();
});
});
};
watchUnclick();
model.activate();
});
我最终做了这样的事情:
$(document).on('click', 'body, #msg_count_results .close',function() {
$(document).find('#msg_count_results').remove();
});
$(document).on('click','#msg_count_results',function(e) {
e.preventDefault();
return false;
});
我在新容器中有一个关闭按钮,用于最终用户友好的 UI 目的。我不得不使用 return false 为了不通过。当然,有一个 HREF 可以带你去某个地方会很好,或者你可以调用一些 ajax 的东西。无论哪种方式,它都适合我。正是我想要的。
这是我为解决问题所做的。
$(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;
}
我只是想让@Pistos 的答案更明显,因为它隐藏在评论中。
这个解决方案对我来说非常有效。纯JS:
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()
假设您要检测用户是否在外部或内部单击的 div 具有 id,例如:“my-special-widget”。
监听body点击事件:
document.body.addEventListener('click', (e) => {
if (isInsideMySpecialWidget(e.target, "my-special-widget")) {
console.log("user clicked INSIDE the widget");
}
console.log("user clicked OUTSIDE the widget");
});
function isInsideMySpecialWidget(elem, mySpecialWidgetId){
while (elem.parentElement) {
if (elem.id === mySpecialWidgetId) {
return true;
}
elem = elem.parentElement;
}
return false;
}
在这种情况下,您不会破坏点击页面中某些元素的正常流程,因为您没有使用“stopPropagation”方法。
另一种解决方案在这里:
用法:
<div onClick="$('#menu').toggle();$('#menu').clickOutside(function() { $(this).hide(); $(this).clickOutside('disable'); });">Open / Close Menu</div>
<div id="menu" style="display: none; border: 1px solid #000000; background: #660000;">I am a menu, whoa is me.</div>
插入:
(function($) {
var clickOutsideElements = [];
var clickListener = false;
$.fn.clickOutside = function(options, ignoreFirstClick) {
var that = this;
if (ignoreFirstClick == null) ignoreFirstClick = true;
if (options != "disable") {
for (var i in clickOutsideElements) {
if (clickOutsideElements[i].element[0] == $(this)[0]) return this;
}
clickOutsideElements.push({ element : this, clickDetected : ignoreFirstClick, fnc : (typeof(options) != "function") ? function() {} : options });
$(this).on("click.clickOutside", function(event) {
for (var i in clickOutsideElements) {
if (clickOutsideElements[i].element[0] == $(this)[0]) {
clickOutsideElements[i].clickDetected = true;
}
}
});
if (!clickListener) {
if (options != null && typeof(options) == "function") {
$('html').click(function() {
for (var i in clickOutsideElements) {
if (!clickOutsideElements[i].clickDetected) {
clickOutsideElements[i].fnc.call(that);
}
if (clickOutsideElements[i] != null) clickOutsideElements[i].clickDetected = false;
}
});
clickListener = true;
}
}
}
else {
$(this).off("click.clickoutside");
for (var i = 0; i < clickOutsideElements.length; ++i) {
if (clickOutsideElements[i].element[0] == $(this)[0]) {
clickOutsideElements.splice(i, 1);
}
}
}
return this;
}
})(jQuery);
标记为已接受答案的答案没有考虑到您可以在元素上覆盖,如对话框、弹出框、日期选择器等。单击这些不应隐藏元素。
我已经制作了自己的版本,它确实考虑到了这一点。它被创建为一个KnockoutJS绑定,但它可以很容易地转换为仅限 jQuery。
它通过第一次查询具有可见的 z-index 或绝对位置的所有元素起作用。然后,如果在外部单击,它会针对我想要隐藏的元素对这些元素进行测试。如果它是一个命中,我会计算一个新的边界矩形,它会考虑覆盖边界。
ko.bindingHandlers.clickedIn = (function () {
function getBounds(element) {
var pos = element.offset();
return {
x: pos.left,
x2: pos.left + element.outerWidth(),
y: pos.top,
y2: pos.top + element.outerHeight()
};
}
function hitTest(o, l) {
function getOffset(o) {
for (var r = { l: o.offsetLeft, t: o.offsetTop, r: o.offsetWidth, b: o.offsetHeight };
o = o.offsetParent; r.l += o.offsetLeft, r.t += o.offsetTop);
return r.r += r.l, r.b += r.t, r;
}
for (var b, s, r = [], a = getOffset(o), j = isNaN(l.length), i = (j ? l = [l] : l).length; i;
b = getOffset(l[--i]), (a.l == b.l || (a.l > b.l ? a.l <= b.r : b.l <= a.r))
&& (a.t == b.t || (a.t > b.t ? a.t <= b.b : b.t <= a.b)) && (r[r.length] = l[i]));
return j ? !!r.length : r;
}
return {
init: function (element, valueAccessor) {
var target = valueAccessor();
$(document).click(function (e) {
if (element._clickedInElementShowing === false && target()) {
var $element = $(element);
var bounds = getBounds($element);
var possibleOverlays = $("[style*=z-index],[style*=absolute]").not(":hidden");
$.each(possibleOverlays, function () {
if (hitTest(element, this)) {
var b = getBounds($(this));
bounds.x = Math.min(bounds.x, b.x);
bounds.x2 = Math.max(bounds.x2, b.x2);
bounds.y = Math.min(bounds.y, b.y);
bounds.y2 = Math.max(bounds.y2, b.y2);
}
});
if (e.clientX < bounds.x || e.clientX > bounds.x2 ||
e.clientY < bounds.y || e.clientY > bounds.y2) {
target(false);
}
}
element._clickedInElementShowing = false;
});
$(element).click(function (e) {
e.stopPropagation();
});
},
update: function (element, valueAccessor) {
var showing = ko.utils.unwrapObservable(valueAccessor());
if (showing) {
element._clickedInElementShowing = true;
}
}
};
})();
对于 iPad 和 iPhone 等触控设备,我们可以使用以下代码:
$(document).on('touchstart', function (event) {
var container = $("YOUR CONTAINER SELECTOR");
if (!container.is(e.target) && // If the target of the click isn't the container...
container.has(e.target).length === 0) // ... nor a descendant of the container
{
container.hide();
}
});
我知道这个问题有一百万个答案,但我一直喜欢使用 HTML 和 CSS 来完成大部分工作。在这种情况下,z-index 和定位。我发现执行此操作的最简单方法如下:
$("#show-trigger").click(function(){
$("#element").animate({width: 'toggle'});
$("#outside-element").show();
});
$("#outside-element").click(function(){
$("#element").hide();
$("#outside-element").hide();
});
#outside-element {
position:fixed;
width:100%;
height:100%;
z-index:1;
display:none;
}
#element {
display:none;
padding:20px;
background-color:#ccc;
width:300px;
z-index:2;
position:relative;
}
#show-trigger {
padding:20px;
background-color:#ccc;
margin:20px auto;
z-index:2;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="outside-element"></div>
<div id="element">
<div class="menu-item"><a href="#1">Menu Item 1</a></div>
<div class="menu-item"><a href="#2">Menu Item 1</a></div>
<div class="menu-item"><a href="#3">Menu Item 1</a></div>
<div class="menu-item"><a href="#4">Menu Item 1</a></div>
</div>
<div id="show-trigger">Show Menu</div>
这创建了一个安全的环境,因为除非菜单实际打开并且 z-index 保护元素中的任何内容在被单击时不会产生任何失火,否则不会触发任何内容。
此外,您不需要 jQuery 用传播调用覆盖所有基础,也不需要清除所有内部元素的失火。
$(document).on("click",function (event)
{
console.log(event);
if ($(event.target).closest('.element').length == 0)
{
//your code here
if ($(".element").hasClass("active"))
{
$(".element").removeClass("active");
}
}
});
尝试此编码以获得解决方案。
这对我有用
$("body").mouseup(function(e) {
var subject = $(".main-menu");
if(e.target.id != subject.attr('id') && !subject.has(e.target).length) {
$('.sub-menu').hide();
}
});
如果您使用“Pop-up”之类的工具,则可以使用“onFocusOut”事件。
window.onload=function(){
document.getElementById("inside-div").focus();
}
function loseFocus(){
alert("Clicked outside");
}
#container{
background-color:lightblue;
width:200px;
height:200px;
}
#inside-div{
background-color:lightgray;
width:100px;
height:100px;
}
<div id="container">
<input type="text" id="inside-div" onfocusout="loseFocus()">
</div>
所有这些答案都解决了这个问题,但我想贡献一个完全符合需要的现代 es6 解决方案。我只是希望能让有人对这个可运行的演示感到满意。
window.clickOutSide = (element, clickOutside, clickInside) => {
document.addEventListener('click', (event) => {
if (!element.contains(event.target)) {
if (typeof clickInside === 'function') {
clickOutside();
}
} else {
if (typeof clickInside === 'function') {
clickInside();
}
}
});
};
window.clickOutSide(document.querySelector('.block'), () => alert('clicked outside'), () => alert('clicked inside'));
.block {
width: 400px;
height: 400px;
background-color: red;
}
<div class="block"></div>
这是我对这个问题找到的最简单的答案:
window.addEventListener('click', close_window = function () {
if(event.target !== windowEl){
windowEl.style.display = "none";
window.removeEventListener('click', close_window, false);
}
});
您会看到我将函数命名为“close_window”,以便在窗口关闭时删除事件侦听器。
一种用纯 JavaScript 编写的方法
let menu = document.getElementById("menu");
document.addEventListener("click", function(){
// Hide the menus
menu.style.display = "none";
}, false);
document.getElementById("menuscontainer").addEventListener("click", function(e){
// Show the menus
menu.style.display = "block";
e.stopPropagation();
}, false);
这对我来说及时很好:
$('body').click(function() {
// Hide the menus if visible.
});
这是我的代码:
// Listen to every click
$('html').click(function(event) {
if ( $('#mypopupmenu').is(':visible') ) {
if (event.target.id != 'click_this_to_show_mypopupmenu') {
$('#mypopupmenu').hide();
}
}
});
// Listen to selector's clicks
$('#click_this_to_show_mypopupmenu').click(function() {
// If the menu is visible, and you clicked the selector again we need to hide
if ( $('#mypopupmenu').is(':visible') {
$('#mypopupmenu').hide();
return true;
}
// Else we need to show the popup menu
$('#mypopupmenu').show();
});
jQuery().ready(function(){
$('#nav').click(function (event) {
$(this).addClass('activ');
event.stopPropagation();
});
$('html').click(function () {
if( $('#nav').hasClass('activ') ){
$('#nav').removeClass('activ');
}
});
});
老实说,我不喜欢以前的任何解决方案。
最好的方法是将“点击”事件绑定到文档,并比较该点击是否真的在元素之外(就像 Art 在他的建议中所说的那样)。
但是,您会遇到一些问题:您将永远无法解除绑定,并且您无法使用外部按钮来打开/关闭该元素。
这就是为什么我写了这个小插件(点击这里链接),以简化这些任务。能不能简单点?
<a id='theButton' href="#">Toggle the menu</a><br/>
<div id='theMenu'>
I should be toggled when the above menu is clicked,
and hidden when user clicks outside.
</div>
<script>
$('#theButton').click(function(){
$('#theMenu').slideDown();
});
$("#theMenu").dClickOutside({ ignoreList: $("#theButton") }, function(clickedObj){
$(this).slideUp();
});
</script>
最广泛的方法是选择网页上的所有内容,除了您不希望检测到点击的元素,并在打开菜单时绑定点击事件。
然后当菜单关闭时删除绑定。
使用 .stopPropagation 防止事件影响菜单容器的任何部分。
$("*").not($("#menuscontainer")).bind("click.OutsideMenus", function ()
{
// hide the menus
//then remove all of the handlers
$("*").unbind(".OutsideMenus");
});
$("#menuscontainer").bind("click.OutsideMenus", function (event)
{
event.stopPropagation();
});
对于某些人来说,这可能是一个更好的解决方案。
$(".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
元素内部,那么菜单本身就不应该成为单击或继续前进的问题。
我相信最好的方法是这样的。
$(document).on("click", function(event) {
clickedtarget = $(event.target).closest('#menuscontainer');
$("#menuscontainer").not(clickedtarget).hide();
});
这种类型的解决方案可以很容易地用于多个菜单以及通过 javascript 动态添加的菜单。基本上它只允许您单击文档中的任何位置,并检查您单击的元素,并选择它最接近的“#menuscontainer”。然后它隐藏所有菜单容器,但不包括您单击的那个。
不确定您的菜单是如何构建的,但请随意在 JSFiddle 中复制我的代码。这是一个非常简单但功能齐全的菜单/模式系统。您需要做的就是构建 html 菜单,代码将为您完成工作。
$('#propertyType').on("click",function(e){
self.propertyTypeDialog = !self.propertyTypeDialog;
b = true;
e.stopPropagation();
console.log("input clicked");
});
$(document).on('click','body:not(#propertyType)',function (e) {
e.stopPropagation();
if(b == true) {
if ($(e.target).closest("#configuration").length == 0) {
b = false;
self.propertyTypeDialog = false;
console.log("outside clicked");
}
}
// console.log($(e.target).closest("#configuration").length);
});
const button = document.querySelector('button')
const box = document.querySelector('.box');
const toggle = event => {
event.stopPropagation();
if (!event.target.closest('.box')) {
console.log('Click outside');
box.classList.toggle('active');
box.classList.contains('active')
? document.addEventListener('click', toggle)
: document.removeEventListener('click', toggle);
} else {
console.log('Click inside');
}
}
button.addEventListener('click', toggle);
.box {
position: absolute;
display: none;
margin-top: 8px;
padding: 20px;
background: lightgray;
}
.box.active {
display: block;
}
<button>Toggle box</button>
<div class="box">
<form action="">
<input type="text">
<button type="button">Search</button>
</form>
</div>
还在寻找检测外部点击的完美解决方案吗?不要再看了!介绍Clickout-Event,这是一个为 clickout 和其他类似事件提供通用支持的包,它适用于所有场景:纯 HTMLonclickout
属性、.addEventListener('clickout')
vanilla JavaScript、.on('clickout')
jQuery、v-on:clickout
Vue.js 指令等等。只要前端框架在内部使用addEventListener
来处理事件,Clickout-Event 就可以为它工作。只需在页面的任何位置添加脚本标签,它就可以像魔术一样工作。
HTML 属性
<div onclickout="console.log('clickout detected')">...</div>
香草 JavaScript
document.getElementById('myId').addEventListener('clickout', myListener);
jQuery
$('#myId').on('clickout', myListener);
Vue.js
<div v-on:clickout="open=false">...</div>
角
<div (clickout)="close()">...</div>
这是一个更通用的解决方案,它允许监视多个元素,并从队列中动态添加和删除元素。
它拥有一个全局队列 ( autoCloseQueue ) - 一个对象容器,用于存放应在外部单击时关闭的元素。
每个队列对象键应该是 DOM 元素 id,值应该是具有 2 个回调函数的对象:
{onPress: someCallbackFunction, onOutsidePress: anotherCallbackFunction}
将其放入您的文档就绪回调中:
window.autoCloseQueue = {}
$(document).click(function(event) {
for (id in autoCloseQueue){
var element = autoCloseQueue[id];
if ( ($(e.target).parents('#' + id).length) > 0) { // This is a click on the element (or its child element)
console.log('This is a click on an element (or its child element) with id: ' + id);
if (typeof element.onPress == 'function') element.onPress(event, id);
} else { //This is a click outside the element
console.log('This is a click outside the element with id: ' + id);
if (typeof element.onOutsidePress == 'function') element.onOutsidePress(event, id); //call the outside callback
delete autoCloseQueue[id]; //remove the element from the queue
}
}
});
然后,当创建 id 为 ' menuscontainer '的 DOM 元素时,只需将此对象添加到队列中:
window.autoCloseQueue['menuscontainer'] = {onOutsidePress: clickOutsideThisElement}
fileTreeClass
如果在其外部单击则 隐藏
jQuery(document).mouseup(function (e) {
var container = $(".fileTreeClass");
if (!container.is(e.target) // if the target of the click isn't the container...
&& container.has(e.target).length === 0) // ... nor a descendant of the container
{
container.hide();
}
});
简单的插件:
$.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;
});
};
采用:
$("#myDiv").clickOff(function() {
alert('clickOff');
});
只是一个警告,使用这个:
$('html').click(function() {
// Hide the menus if visible
});
$('#menucontainer').click(function(event){
event.stopPropagation();
});
它会阻止Ruby on Rails UJS 驱动程序正常工作。例如,link_to 'click', '/url', :method => :delete
不会工作。
这可能是一种解决方法:
$('html').click(function() {
// Hide the menus if visible
});
$('#menucontainer').click(function(event){
if (!$(event.target).data('method')) {
event.stopPropagation();
}
});
当您单击/关闭元素时,这将切换导航菜单。
$(document).on('click', function(e) {
var elem = $(e.target).closest('#menu'),
box = $(e.target).closest('#nav');
if (elem.length) {
e.preventDefault();
$('#nav').toggle();
} else if (!box.length) {
$('#nav').hide();
}
});
<li id="menu"><a></a></li>
<ul id="nav" > //Nav will toggle when you Click on Menu(it can be an icon in this example)
<li class="page"><a>Page1</a></li>
<li class="page"><a>Pag2</a></li>
<li class="page"><a>Page3</a></li>
<li class="page"><a>Page4</a></li>
</ul>
外点击插件!
用法:
$('.target-element').outsideClick(function(event){
//code that fires when user clicks outside the element
//event = the click event
//$(this) = the '.target-element' that is firing this function
}, '.excluded-element')
它的代码:
(function($) {
//when the user hits the escape key, it will trigger all outsideClick functions
$(document).on("keyup", function (e) {
if (e.which == 27) $('body').click(); //escape key
});
//The actual plugin
$.fn.outsideClick = function(callback, exclusions) {
var subject = this;
//test if exclusions have been set
var hasExclusions = typeof exclusions !== 'undefined';
//switches click event with touch event if on a touch device
var ClickOrTouchEvent = "ontouchend" in document ? "touchend" : "click";
$('body').on(ClickOrTouchEvent, function(event) {
//click target does not contain subject as a parent
var clickedOutside = !$(event.target).closest(subject).length;
//click target was on one of the excluded elements
var clickedExclusion = $(event.target).closest(exclusions).length;
var testSuccessful;
if (hasExclusions) {
testSuccessful = clickedOutside && !clickedExclusion;
} else {
testSuccessful = clickedOutside;
}
if(testSuccessful) {
callback.call(subject, event);
}
});
return this;
};
}(jQuery));
$('html').click(function() {
//Hide the menus if visible
});
$('#menucontainer').click(function(event){
event.stopPropagation();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
<button id='#menucontainer'>Ok</button>
</html>
试试这个:
$('html').click(function(e) {
if($(e.target).parents('#menuscontainer').length == 0) {
$('#menuscontainer').hide();
}
});
https://jsfiddle.net/4cj4jxy0/
但请注意,如果点击事件无法到达html
标签,这将无法正常工作。(也许其他元素有stopPropagation()
)。
订阅点击的捕获阶段以处理对调用的元素的点击preventDefault
。
使用其他名称在文档元素上重新触发它click-anywhere
。
document.addEventListener('click', function (event) {
event = $.event.fix(event);
event.type = 'click-anywhere';
$document.trigger(event);
}, true);
然后你需要点击外部功能订阅click-anywhere
事件document
并检查点击是否在你感兴趣的元素之外:
$(document).on('click-anywhere', function (event) {
if (!$(event.target).closest('#smth').length) {
// Do anything you need here
}
});
一些注意事项:
您必须使用document
,因为在单击发生之外的所有元素上触发事件将是性能错误。
此功能可以包装到特殊插件中,该插件在外部点击时调用一些回调。
您不能使用 jQuery 本身订阅捕获阶段。
您不需要文档加载来订阅,因为订阅是 on document
,即使不是订阅body
,所以它总是独立存在 ащкь 脚本放置和加载状态。
$(document).on('click.menu.hide', function(e){
if ( !$(e.target).closest('#my_menu').length ) {
$('#my_menu').find('ul').toggleClass('active', false);
}
});
$(document).on('click.menu.show', '#my_menu li', function(e){
$(this).find('ul').toggleClass('active');
});
div {
float: left;
}
ul {
padding: 0;
position: relative;
}
ul li {
padding: 5px 25px 5px 10px;
border: 1px solid silver;
cursor: pointer;
list-style: none;
margin-top: -1px;
white-space: nowrap;
}
ul li ul:before {
margin-right: -20px;
position: absolute;
top: -17px;
right: 0;
content: "\25BC";
}
ul li ul li {
visibility: hidden;
height: 0;
padding-top: 0;
padding-bottom: 0;
border-width: 0 0 1px 0;
}
ul li ul li:last-child {
border: none;
}
ul li ul.active:before {
content: "\25B2";
}
ul li ul.active li {
display: list-item;
visibility: visible;
height: inherit;
padding: 5px 25px 5px 10px;
}
<script src="https://code.jquery.com/jquery-2.1.4.js"></script>
<div>
<ul id="my_menu">
<li>Menu 1
<ul>
<li>subMenu 1</li>
<li>subMenu 2</li>
<li>subMenu 3</li>
<li>subMenu 4</li>
</ul>
</li>
<li>Menu 2
<ul>
<li>subMenu 1</li>
<li>subMenu 2</li>
<li>subMenu 3</li>
<li>subMenu 4</li>
</ul>
</li>
<li>Menu 3</li>
<li>Menu 4</li>
<li>Menu 5</li>
<li>Menu 6</li>
</ul>
</div>
这是 jsbin 版本http://jsbin.com/xopacadeni/edit?html,css,js,output
如果您只想在单击按钮时显示一个窗口,并在单击外部时取消显示此窗口。(或再次单击按钮)此波纹管效果很好
document.body.onclick = function() { undisp_menu(); };
var menu_on = 0;
function menu_trigger(event){
if (menu_on == 0)
{
// otherwise u will call the undisp on body when
// click on the button
event.stopPropagation();
disp_menu();
}
else{
undisp_menu();
}
}
function disp_menu(){
menu_on = 1;
var e = document.getElementsByClassName("menu")[0];
e.className = "menu on";
}
function undisp_menu(){
menu_on = 0;
var e = document.getElementsByClassName("menu")[0];
e.className = "menu";
}
不要忘记这个按钮
<div class="button" onclick="menu_trigger(event)">
<div class="menu">
和CSS:
.menu{
display: none;
}
.on {
display: inline-block;
}
这对我来说很好。我不是专家。
$(document).click(function(event) {
var $target = $(event.target);
if(!$target.closest('#hamburger, a').length &&
$('#hamburger, a').is(":visible")) {
$('nav').slideToggle();
}
});
我已经阅读了 2021 年的所有内容,但如果没有错的话,没有人建议像这样简单的方法来取消绑定和删除事件。使用上述两个答案和一个小技巧,将所有内容合二为一(也可以将参数添加到函数中以传递选择器,以获得更多弹出窗口)。可能有人知道这个笑话也可以这样做是有用的:
<div id="container" style="display:none"><h1>my menu is nice but disappear if i click outside it</h1></div>
<script>
function printPopup(){
$("#container").css({ "display":"block" });
var remListener = $(document).mouseup(function (e) {
if ($(e.target).closest("#container").length === 0 && (e.target != $('html').get(0)))
{
//alert('closest call');
$("#container").css({ "display":"none" });
remListener.unbind('mouseup'); // isn't it?
}
});
}
printPopup();
</script>
干杯
你不需要(很多)JavaScript,只需要:focus-within
选择器:
.sidebar:focus-within
显示您的侧边栏。tabindex=-1
在您的侧边栏和正文元素上,以使它们具有焦点。sidebarEl.focus()
使用和设置侧边栏可见性document.body.focus()
。const menuButton = document.querySelector('.menu-button');
const sidebar = document.querySelector('.sidebar');
menuButton.onmousedown = ev => {
ev.preventDefault();
(sidebar.contains(document.activeElement) ?
document.body : sidebar).focus();
};
* { box-sizing: border-box; }
.sidebar {
position: fixed;
width: 15em;
left: -15em;
top: 0;
bottom: 0;
transition: left 0.3s ease-in-out;
background-color: #eef;
padding: 3em 1em;
}
.sidebar:focus-within {
left: 0;
}
.sidebar:focus {
outline: 0;
}
.menu-button {
position: fixed;
top: 0;
left: 0;
padding: 1em;
background-color: #eef;
border: 0;
}
body {
max-width: 30em;
margin: 3em;
}
<body tabindex='-1'>
<nav class='sidebar' tabindex='-1'>
Sidebar content
<input type="text"/>
</nav>
<button class="menu-button">☰</button>
Body content goes here, Lorem ipsum sit amet, etc
</body>
对于那些想要一个简短的解决方案来集成到他们的 JS 代码中的人 - 一个没有 JQuery 的小型库:
用法:
// demo code
var htmlElem = document.getElementById('my-element')
function doSomething(){ console.log('outside click') }
// use the lib
var removeListener = new elemOutsideClickListener(htmlElem, doSomething);
// deregister on your wished event
$scope.$on('$destroy', removeListener);
这是库:
function elemOutsideClickListener (element, outsideClickFunc, insideClickFunc) {
function onClickOutside (e) {
var targetEl = e.target; // clicked element
do {
// click inside
if (targetEl === element) {
if (insideClickFunc) insideClickFunc();
return;
// Go up the DOM
} else {
targetEl = targetEl.parentNode;
}
} while (targetEl);
// click outside
if (!targetEl && outsideClickFunc) outsideClickFunc();
}
window.addEventListener('click', onClickOutside);
return function () {
window.removeEventListener('click', onClickOutside);
};
}
我从这里获取代码并将其放入一个函数中: https ://www.w3docs.com/snippets/javascript/how-to-detect-a-click-outside-an-element.html
作为来自 Art 的这个出色答案的包装,并使用 OP 最初请求的语法,这里有一个 jQuery 扩展,可以记录是否在 set 元素之外发生了点击。
$.fn.clickOutsideThisElement = function (callback) {
return this.each(function () {
var self = this;
$(document).click(function (e) {
if (!$(e.target).closest(self).length) {
callback.call(self, e)
}
})
});
};
然后你可以这样调用:
$("#menuscontainer").clickOutsideThisElement(function() {
// handle menu toggle
});
$('#menucontainer').click(function(e){
e.stopPropagation();
});
$(document).on('click', function(e){
// code
});
首先,您必须使用 mouseenter 和 mouseleave 事件来跟踪鼠标是在 element1 内部还是外部。然后,您可以创建一个覆盖整个屏幕的 element2 以检测任何点击,并根据您是在 element1 内部还是外部做出相应的反应。
我强烈建议同时处理初始化和清理,并且由于显而易见的原因,element2 尽可能地是临时的。
在下面的示例中,叠加层是位于某处的元素,可以通过单击内部来选择它,并且可以通过单击外部来取消选择。_init 和 _release 方法被称为自动初始化/清理过程的一部分。该类继承自具有内部和外部元素的 ClickOverlay,不用担心。我使用了 outerElement.parentNode.appendChild 来避免冲突。
import ClickOverlay from './ClickOverlay.js'
/* CSS */
// .unselect-helper {
// position: fixed; left: -100vw; top: -100vh;
// width: 200vw; height: 200vh;
// }
// .selected {outline: 1px solid black}
export default class ResizeOverlay extends ClickOverlay {
_init(_opts) {
this.enterListener = () => this.onEnter()
this.innerElement.addEventListener('mouseenter', this.enterListener)
this.leaveListener = () => this.onLeave()
this.innerElement.addEventListener('mouseleave', this.leaveListener)
this.selectListener = () => {
if (this.unselectHelper)
return
this.unselectHelper = document.createElement('div')
this.unselectHelper.classList.add('unselect-helper')
this.unselectListener = () => {
if (this.mouseInside)
return
this.clearUnselectHelper()
this.onUnselect()
}
this.unselectHelper.addEventListener('pointerdown'
, this.unselectListener)
this.outerElement.parentNode.appendChild(this.unselectHelper)
this.onSelect()
}
this.innerElement.addEventListener('pointerup', this.selectListener)
}
_release() {
this.innerElement.removeEventListener('mouseenter', this.enterListener)
this.innerElement.removeEventListener('mouseleave', this.leaveListener)
this.innerElement.removeEventListener('pointerup', this.selectListener)
this.clearUnselectHelper()
}
clearUnselectHelper() {
if (!this.unselectHelper)
return
this.unselectHelper.removeEventListener('pointerdown'
, this.unselectListener)
this.unselectHelper.remove()
delete this.unselectListener
delete this.unselectHelper
}
onEnter() {
this.mouseInside = true
}
onLeave() {
delete this.mouseInside
}
onSelect() {
this.innerElement.classList.add('selected')
}
onUnselect() {
this.innerElement.classList.remove('selected')
}
}
最简单的方法:mouseleave(function())
<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();
}
这是我想出的最简单的解决方案。
试试这个代码:
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);
}
使用非():
$("#id").not().click(function() {
alert('Clicked other that #id');
});
$("body > div:not(#dvid)").click(function (e) {
//your code
});
$("html").click(function(){
if($('#info').css("opacity")>0.9) {
$('#info').fadeOut('fast');
}
});
这是一个经典案例,其中对 HTML 进行调整会是更好的解决方案。为什么不对不包含菜单项的元素设置点击?然后你不需要添加传播。
$('.header, .footer, .main-content').click(function() {
//Hide the menus if visible
});