我有一些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();
}
});
如果您打算关闭菜单并想要停止侦听事件,也可以在事件监听器之后进行清理.此函数将仅清除新创建的侦听器,并保留其他任何单击侦听器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)
}
对于那些不想使用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
注意:使用
stopEventPropagation()
是应该避免的,因为它破坏了DOM中的正常事件流.有关更多信息,请参阅此文章.考虑使用这种方法,而不是
将单击事件附加到关闭窗口的文档正文.将单独的单击事件附加到容器,该容器将停止传播到文档正文.
$(window).click(function() { //Hide the menus if visible }); $('#menucontainer').click(function(event){ event.stopPropagation(); });
您可以侦听单击事件document
,然后通过使用确保#menucontainer
不是祖先或单击元素的目标 .closest()
.
如果不是,则单击的元素位于其外部,#menucontainer
您可以安全地隐藏它.
$(document).click(function(event) {
$target = $(event.target);
if(!$target.closest('#menucontainer').length &&
$('#menucontainer').is(":visible")) {
$('#menucontainer').hide();
}
});
如果您打算关闭菜单并想要停止侦听事件,也可以在事件监听器之后进行清理.此函数将仅清除新创建的侦听器,并保留其他任何单击侦听器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)
}
对于那些不想使用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
如何检测元素外的点击?
这个问题如此受欢迎并且答案如此之多的原因在于它看起来很复杂.经过近八年的时间和几十个答案,我真的很惊讶地看到对可访问性的关注度很低.
当用户点击菜单区域外时,我想隐藏这些元素.
这是一个崇高的事业,也是实际问题.问题的标题 - 大多数答案似乎试图解决 - 包含一个不幸的红鲱鱼.
提示:这是"点击"这个词!
您实际上并不想绑定点击处理程序.如果您绑定了点击处理程序以关闭对话框,那么您已经失败了.你失败的原因是不是每个人都会触发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;
}
Example
Lorem ipsum dolor sit amet.
这里的其他解决方案对我不起作用,所以我不得不使用:
if(!$(event.target).is('#foo')) { // hide menu }
我有一个与Eran的例子类似的应用程序,除了我打开菜单时将click事件附加到正文...有点像这样:
$('#menucontainer').click(function(event) { $('html').one('click',function() { // Hide the menus }); event.stopPropagation(); });
有关jQuery 功能的更多信息one()
经过研究,我找到了三种工作方案(我忘了页面链接供参考)
$("#menuscontainer").click(function() { $(this).focus(); }); $("#menuscontainer").blur(function(){ $(this).hide(); });
对我来说就好了.
现在有一个插件:外部事件(博客文章)
将clickoutside处理程序(WLOG)绑定到元素时会发生以下情况:
该元素被添加到一个数组中,该数组包含clickoutside处理程序的所有元素
a(命名空间)单击处理程序绑定到文档(如果尚未存在)
对于文档中的任何单击,触发clickoutside事件,以便该数组中那些不等于或者是click -events目标的父元素的元素
另外,clickoutside事件的event.target 设置为用户点击的元素(这样你甚至可以知道用户点击了什么,而不仅仅是他点击了外面)
因此,没有事件停止传播,并且可以在具有外部处理程序的元素"上方"使用其他点击处理程序.
这完全适合我!
$('html').click(function (e) { if (e.target.id == 'YOUR-DIV-ID') { //do something } else { //do something } });
我不认为你真正需要的是当用户点击外面时关闭菜单; 您需要的是当用户点击页面上的任何位置时关闭菜单.如果您单击菜单,或关闭菜单,它应该关闭吗?
上面没有找到满意的答案促使我前几天写这篇博文.对于更迂腐的人来说,有许多值得注意的问题:
如果在单击时将click事件处理程序附加到body元素,请确保在关闭菜单之前等待第二次单击,并取消绑定事件.否则,打开菜单的单击事件将冒泡到必须关闭菜单的侦听器.
如果对click事件使用event.stopPropogation(),则页面中没有其他元素可以具有click-anywhere-to-close功能.
将click事件处理程序无限期地附加到body元素不是一个高性能的解决方案
将事件的目标及其父项与处理程序的创建者进行比较假定您想要的是在单击它时关闭菜单,当您真正想要的是在单击页面上的任何位置时关闭它.
在body元素上侦听事件会使代码更加脆弱.造型像无辜一样会破坏它: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); } }) });
这种情况的简单解决方案是:
$(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
if div
click事件的外部是否被触发.
您可以在以下博客中查看更多信息:http://www.codecanal.com/detect-click-outside-div-using-javascript/
而不是使用可能有一些副作用的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 } });
检查窗口单击事件目标(它应传播到窗口,只要它没有在其他任何地方捕获),并确保它不是任何菜单元素.如果不是,那么你就在菜单之外了.
或者检查点击的位置,看看它是否包含在菜单区域中.
我有这样的成功:
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
仅在目标(单击)不是它的子项时隐藏的主体.
作为变种:
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);
该事件有一个名为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;
}
- Test-Main
- Test-Main
- Test-Main
- Test-Main
- Test-Main
Outside Main
这是针对未来观众的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);
})();
Toggle Hidden Div
This content is normally hidden. click anywhere other than this content to make me disappear
我很惊讶没有人真正认可的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 = '';
});
如果你是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");
在文档上挂钩单击事件侦听器.在事件侦听器中,您可以查看事件对象,特别是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"); } });
$(document).click(function() { $(".overlay-window").hide(); }); $(".overlay-window").click(function() { return false; });
如果单击文档,则隐藏给定元素,除非您单击该元素.
我在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(); } });
我正在检查祖先是不是小部件元素容器,
如果目标不是打开小部件/元素,
如果我要关闭的小部件/元素已经打开(不那么重要).
Upvote为最受欢迎的答案,但添加
&& (e.target != $('html').get(0)) // ignore the scrollbar
因此,单击滚动条不会[隐藏或不管]您的目标元素.
为了更容易使用,以及更具表现力的代码,我为此创建了一个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... });
如果有人好奇这里是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代码
Hello I am the titleI will hide when you click outside of me
您可以在这里阅读教程