如何检测元素外部的点击?
我有一些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"的主体。