如何检测元素外部的点击?
我有一些HTML菜单,当用户单击这些菜单的标题时,它们会完整显示。当用户在菜单区域之外单击时,我想隐藏这些元素。
jQuery可能会发生这种情况吗?
$("#menuscontainer").clickOutsideThisElement(function() {
// Hide the menus
});
解决方案
检查窗口单击事件目标(只要没有在其他任何地方捕获,它应该传播到窗口),并确保它不是任何菜单元素。如果不是,那么我们就在菜单之外。
或者检查单击的位置,然后查看它是否包含在菜单区域中。
NOTE: Using stopEventPropagation() is something that should be avoided as it breaks normal event flow in the DOM. See this article for more information. Consider using this method instead.
将单击事件添加到关闭窗口的文档主体。将单独的click事件添加到容器,以停止传播到文档主体。
$(window).click(function() {
//Hide the menus if visible
});
$('#menucontainer').click(function(event){
event.stopPropagation();
});
我有一个与Eran示例类似的应用程序,除了在打开菜单时将click事件添加到主体上。
$('#menucontainer').click(function(event) {
$('html').one('click',function() {
// Hide the menus
});
event.stopPropagation();
});
有关jQueryone()函数的更多信息
这里的其他解决方案对我不起作用,所以我不得不使用:
if(!$(event.target).is('#foo'))
{
// hide menu
}
如果我们正在为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;}
$("#menuscontainer").click(function() {
$(this).focus();
});
$("#menuscontainer").blur(function(){
$(this).hide();
});
对我有用。
现在有一个用于此的插件:外部事件(博客文章)
将clickoutside处理程序(WLOG)绑定到元素时,将发生以下情况:
- 将该元素添加到一个数组,该数组包含带有clickoutside处理程序的所有元素
- (命名空间的)单击处理程序已绑定到文档(如果尚不存在)
- 在文档中的任何单击上,都会为该数组中与click-events目标不相等或者不是其父元素的那些元素触发clickoutside事件
- 此外,clickoutside事件的event.target设置为用户单击的元素(因此,我们甚至知道用户单击了什么,而不仅仅是他在外部单击了)
因此,不会停止任何事件的传播,并且可以在外部处理程序的"上方"使用其他单击处理程序。
这对我来说非常正常:
$('body').click(function() {
// Hide the menus if visible.
});
我们可以使用.closest()来监听document上的click事件,然后确保#menucontainer不是被单击元素的祖先或者目标。
如果不是,则单击的元素在#menucontainer之外,我们可以安全地将其隐藏。
$(document).click(function(event) {
if(!$(event.target).closest('#menucontainer').length) {
if($('#menucontainer').is(":visible")) {
$('#menucontainer').hide();
}
}
});
编辑2017-06-23
如果我们打算关闭菜单并想停止监听事件,则还可以在事件监听器之后进行清理。此功能将仅清理新创建的侦听器,并在document上保留所有其他单击侦听器。使用ES2015语法:
export function hideOnClickOutside(selector) {
const outsideClickListener = (event) => {
if (!$(event.target).closest(selector).length) {
if ($(selector).is(':visible')) {
$(selector).hide()
removeClickListener()
}
}
}
const removeClickListener = () => {
document.removeEventListener('click', outsideClickListener)
}
document.addEventListener('click', outsideClickListener)
}
编辑2018-03-11
对于那些不想使用jQuery的人。这是上面的纯香草代码(ECMAScript6)中的代码。
function hideOnClickOutside(element) {
const outsideClickListener = event => {
if (!element.contains(event.target)) { // or use: event.target.closest(selector) === null
if (isVisible(element)) {
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
功能:
$(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();
}
我在以下方面取得了成功:
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"的主体。

