提问人: 提问时间:9/30/2008 最后编辑:6 revs, 3 users 50%Sergio del Amo 更新时间:3/28/2023 访问量:1720603
如何检测元素外部的点击?
How do I detect a click outside an element?
问:
我有一些 HTML 菜单,当用户单击这些菜单的头部时,我会完全显示这些菜单。我想在用户单击菜单区域之外时隐藏这些元素。
jQuery可以做这样的事情吗?
$("#menuscontainer").clickOutsideThisElement(function() {
// Hide the menus
});
答:
检查窗口单击事件目标(只要它未在其他任何地方捕获,它就应传播到窗口),并确保它不是任何菜单元素。如果不是,那么你就不在菜单范围内。
或者检查单击的位置,并查看它是否包含在菜单区域中。
注意:使用是应该避免的,因为它会破坏 DOM 中的正常事件流。有关详细信息,请参阅此 CSS 技巧文章。请考虑改用此方法。
stopPropagation
将单击事件附加到文档正文,以关闭窗口。将单独的单击事件附加到容器,以停止传播到文档正文。
$(window).click(function() {
//Hide the menus if visible
});
$('#menucontainer').click(function(event){
event.stopPropagation();
});
评论
$('html').click()
我有一个应用程序,其工作方式与 Eran 的示例类似,只是在打开菜单时将单击事件附加到正文......有点像这样:
$('#menucontainer').click(function(event) {
$('html').one('click',function() {
// Hide the menus
});
event.stopPropagation();
});
有关 jQuery 的 one()
函数的更多信息
评论
这里的其他解决方案对我不起作用,所以我不得不使用:
if(!$(event.target).is('#foo'))
{
// hide menu
}
编辑:纯Javascript变体(2021-03-31)
我使用这种方法来处理在单击下拉菜单时关闭下拉菜单。
首先,我为组件的所有元素创建了一个自定义类名。此类名称将添加到构成菜单小部件的所有元素中。
const className = `dropdown-${Date.now()}-${Math.random() * 100}`;
我创建了一个函数来检查点击和点击元素的类名。如果单击的元素不包含我在上面生成的自定义类名,则应将标志设置为,菜单将关闭。show
false
const onClickOutside = (e) => {
if (!e.target.className.includes(className)) {
show = false;
}
};
然后,我将单击处理程序附加到 window 对象。
// add when widget loads
window.addEventListener("click", onClickOutside);
...最后是一些家政服务
// remove listener when destroying the widget
window.removeEventListener("click", onClickOutside);
评论
&& !$(event.target).parents("#foo").is("#foo")
IF
如果您正在为 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 目标或不属于 Click-Events 目标的元素触发 ClickOutside 事件
- 此外,clickoutside 事件的 event.target 设置为用户单击的元素(因此您甚至可以知道用户单击了什么,而不仅仅是他在外部单击)
因此,不会阻止任何事件的传播,并且可以在元素的“上方”使用额外的单击处理程序和外部处理程序。
这对我来说效果很好:
$('body').click(function() {
// Hide the menus if visible.
});
评论
您可以侦听 click 事件,然后使用 .closest()
确保不是 click 元素的祖先或目标。document
#menucontainer
如果不是,则单击的元素位于 之外,您可以安全地隐藏它。#menucontainer
$(document).click(function(event) {
var $target = $(event.target);
if(!$target.closest('#menucontainer').length &&
$('#menucontainer').is(":visible")) {
$('#menucontainer').hide();
}
});
编辑 – 2017-06-23
如果您计划关闭菜单并希望停止侦听事件,也可以在事件侦听器之后进行清理。此函数将仅清理新创建的侦听器,保留 上的任何其他单击侦听器。使用 ES2015 语法:document
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);
}
编辑 - 2018-03-11
对于那些不想使用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 注释,仅使用而不是 jQuery 部分。!element.contains(event.target)
但现在也可以在所有主流浏览器中使用(W3C 版本与 jQuery 版本略有不同)。
Polyfills 可以在这里找到:Element.closest()element.closest()
编辑 - 2020-05-21
如果您希望用户能够在元素内部单击并拖动,然后在元素外部释放鼠标,而不关闭元素:
...
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()
}
}
评论
功能:
$(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
用:
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();
});
我不认为您真正需要的是当用户单击外部时关闭菜单;您需要的是当用户单击页面上的任意位置时关闭菜单。如果你点击菜单,或者离开菜单,它应该关闭,对吧?
上面没有找到令人满意的答案,这促使我前几天写了这篇博文。对于更迂腐的人来说,有许多陷阱需要注意:
- 如果在单击时将单击事件处理程序附加到 body 元素,请确保在关闭菜单并取消绑定事件之前等待第二次单击。否则,打开菜单的单击事件将冒泡到必须关闭菜单的侦听器。
- 如果对点击事件使用 event.stopPropogation(),则页面中的其他元素都不能具有“单击任意位置关闭”功能。
- 将单击事件处理程序无限期地附加到 body 元素不是高性能的解决方案
- 将事件的目标及其父级与处理程序的创建者进行比较,假定您想要的是在单击菜单时关闭菜单,而真正想要的是在单击页面上的任意位置时关闭菜单。
- 侦听 body 元素上的事件会使代码更加脆弱。像这样无辜的造型会破坏它:
body { margin-left:auto; margin-right: auto; width:960px;}
评论
我在一些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);
$(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();
}
});
我正在检查祖先是否不是小部件元素容器,如果目标不是打开的小部件/元素,
如果我想关闭的小部件/元素已经打开(不是那么重要)。
正如另一位发帖人所说,有很多陷阱,特别是如果你显示的元素(在本例中为菜单)具有交互式元素。 我发现以下方法相当强大:
$('#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);
}
})
});
这是我的代码:
// 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();
});
这是我对这个问题的解决方案:
$(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();
});
});
jQuery().ready(function(){
$('#nav').click(function (event) {
$(this).addClass('activ');
event.stopPropagation();
});
$('html').click(function () {
if( $('#nav').hasClass('activ') ){
$('#nav').removeClass('activ');
}
});
});
在文档上挂接单击事件侦听器。在事件侦听器中,您可以查看事件对象,特别是 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");
}
});
这对我非常有效!
$('html').click(function (e) {
if (e.target.id == 'YOUR-DIV-ID') {
//do something
} else {
//do something
}
});
老实说,我不喜欢以前的任何解决方案。
最好的方法是将“click”事件绑定到文档,并比较该单击是否真的在元素之外(就像 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>
只是一个警告,使用这个:
$('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();
}
});
评论
这应该有效:
$('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
}
});
评论
<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();
}
这是我想出的最简单的解决方案。
评论
<div class="feedbackCont">
tabindex
<div class="feedbackhide">
这里还有一种解决方案:
用法:
<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);
最广泛的方法是选择网页上的所有内容,除了您不希望检测到点击的元素之外,并在打开菜单时绑定点击事件。
然后,当菜单关闭时,删除绑定。
使用 .stopPropagation 可防止事件影响 menus容器的任何部分。
$("*").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();
});
当只管理一个元素时,这里的解决方案可以正常工作。但是,如果有多个元素,问题就复杂得多。使用 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.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");
标记为已接受答案的答案没有考虑到您可以在元素上覆盖,例如对话框、弹出框、日期选择器等。点击这些内容不应隐藏元素。
我制作了自己的版本,确实考虑到了这一点。它是作为 KnockoutJS 绑定创建的,但它可以很容易地转换为仅 jQuery。
它通过对所有具有 z 索引或绝对位置的可见元素进行第一次查询来工作。然后,如果单击外部,它会针对我想要隐藏的元素测试这些元素。如果是命中,我会计算一个新的绑定矩形,该矩形考虑了覆盖边界。
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;
}
}
};
})();
这是一个更通用的解决方案,允许监视多个元素,并在队列中动态添加和删除元素。
它包含一个全局队列 (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}
我最终做了这样的事情:
$(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 才能不通过。当然,有一个 A HREF 可以带你去某个地方会很好,或者你可以打电话给一些 ajax 的东西。无论哪种方式,它对我来说都很好用。正是我想要的。
试试这个代码:
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);
}
您可以将 tabindex 设置为 DOM 元素。当用户在 DOM 元素外部单击时,这将触发模糊事件。
<div tabindex="1">
Focus me
</div>
document.querySelector("div").onblur = function(){
console.log('clicked outside')
}
document.querySelector("div").onfocus = function(){
console.log('clicked inside')
}
评论
这将在您单击元素时切换导航菜单。
$(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();
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<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>Page2</a></li>
<li class="page"><a>Page3</a></li>
<li class="page"><a>Page4</a></li>
</ul>
标准 HTML:
将菜单包围起来,并获取焦点状态更改。<label>
另外:您可以通过以下方式展开菜单。Tab
评论
作为 Art 这个伟大答案的包装,并使用 OP 最初请求的语法,这里有一个 jQuery 扩展,它可以记录在设置元素之外是否发生了点击。
$.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
});
这是一个小提琴演示
作为变体:
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 解决方案中保持打开状态。
对于 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();
}
});
使用 not():
$("#id").not().click(function() {
alert('Clicked other that #id');
});
评论
not()
$('#id')
$("body > div:not(#dvid)").click(function (e) {
//your code
});
评论
$("html").click(function(){
if($('#info').css("opacity")>0.9) {
$('#info').fadeOut('fast');
}
});
评论
解决方案1
与其使用 event.stopPropagation() 可能会产生一些副作用,只需定义一个简单的标志变量并添加一个条件即可。我对此进行了测试并正常工作,没有对 stopPropagation 产生任何副作用:if
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
}
});
评论
这是面向未来观众的普通 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>
我们实施了一个解决方案,部分基于上面用户的评论,这对我们来说非常有效。当点击这些元素之外时,我们使用它来隐藏搜索框/结果,不包括原来的元素。
// 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');
}
});
它首先检查搜索框是否已经可见,在我们的例子中,它还删除了隐藏/显示搜索按钮上的活动类。
为最受欢迎的答案投票,但添加
&& (e.target != $('html').get(0)) // ignore the scrollbar
因此,单击滚动条不会[隐藏或其他]您的目标元素。
评论
这是一个经典的案例,其中对 HTML 进行调整将是一个更好的解决方案。为什么不设置对不包含菜单项的元素的单击?然后,您无需添加传播。
$('.header, .footer, .main-content').click(function() {
//Hide the menus if visible
});
评论
外部点击插件!
用法:
$('.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));
改编自此答案 https://stackoverflow.com/a/3028037/1611058
评论
ClickOrTouchEvent
经过研究,我找到了三种可行的解决方案
第一个解决方案
<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>
评论
$('#menucontainer').click(function(e){
e.stopPropagation();
});
$(document).on('click', function(e){
// code
});
评论
这种情况的简单解决方案是:
$(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();
}
});
上面的脚本将隐藏 if outside of the click 事件被触发。div
div
$('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>
评论
#menucontainer
//Hide the menus if visible
试试这个:
$('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 本身订阅捕获阶段。
您不需要文档加载来订阅,因为订阅处于开启状态,甚至不需要在其 上,因此它始终独立存在脚本放置和加载状态。
document
body
评论
capture
stopPropagation()
为了更易于使用和更具表现力的代码,我为此创建了一个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...
});
$(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
评论
如何检测元素外部的点击?
这个问题之所以如此受欢迎,并且有如此多的答案,是因为它看似复杂。经过将近八年的时间和数十个答案,我真的很惊讶地发现对可访问性的关注如此之少。
我想在用户单击菜单区域之外时隐藏这些元素。
这是一项崇高的事业,也是实际问题。这个问题的标题——这似乎是大多数答案试图解决的问题——包含了一个不幸的红鲱鱼。
提示:这是“点击”这个词!
您实际上并不想绑定单击处理程序。
如果绑定单击处理程序以关闭对话框,则已失败。你失败的原因是不是每个人都会触发事件。不使用鼠标的用户将能够通过按 来转出您的对话框(您的弹出菜单可以说是一种对话框),然后他们将无法读取对话框后面的内容,而不会随后触发事件。click
Tabclick
因此,让我们重新表述这个问题。
当用户完成对话时,如何关闭对话?
这就是我们的目标。不幸的是,现在我们需要绑定事件,而绑定并不那么简单。userisfinishedwiththedialog
那么,我们如何检测用户是否已经完成了对对话的使用呢?
focusout
事件
一个好的开始是确定焦点是否已离开对话框。
提示:小心模糊事件,如果事件绑定到冒泡阶段,
则模糊
不会传播!
jQuery的focusout
就可以了。如果不能使用 jQuery,则可以在捕获阶段使用:blur
element.addEventListener('blur', ..., true);
// use capture: ^^^^
此外,对于许多对话框,需要允许容器获得焦点。添加以允许对话框动态接收焦点,而不会中断 Tab 键流。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>
如果您玩该演示超过一分钟,您应该很快就会开始看到问题。
首先是对话框中的链接不可点击。尝试单击它或按 Tab 键切换到它将导致对话框在交互发生之前关闭。这是因为在再次触发事件之前,聚焦内部元素会触发事件。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钥匙
如果您认为处理焦点状态已经完成,那么您可以采取更多措施来简化用户体验。
这通常是一个“很高兴拥有”的功能,但通常当您拥有任何类型的模态或弹出窗口时,键会将其关闭。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 角色和其他辅助功能支持
这个答案希望涵盖了此功能的可访问键盘和鼠标支持的基础知识,但由于它已经相当大了,我将避免对 WAI-ARIA 角色和属性进行任何讨论,但我强烈建议实现者参考规范,了解他们应该使用哪些角色和任何其他适当的属性的详细信息。
评论
You don't actually want to bind click handlers.
您可以绑定单击处理程序,还可以处理用户没有鼠标的情况。它不会损害可访问性,它只是为使用鼠标的用户添加功能。向一组用户添加功能不会伤害无法使用该功能的用户。您可以提供多种关闭 diablog 的方法 这实际上是一个非常常见的逻辑谬误。为一组用户提供功能是完全可以的,即使其他用户没有受益。我同意所有用户都应该能够获得良好的体验
blur
focusout
要在单击外部时隐藏: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');
});
评论
对于某些人来说,这可能是一个更好的解决方案。
$(".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 菜单,代码将为您完成工作。
https://jsfiddle.net/zs6anrn7/
我知道这个问题有一百万个答案,但我一直喜欢使用 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");
}
}
});
尝试使用此编码来获取解决方案。
该事件具有一个名为 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
}
});
如果有人好奇,这里有一个 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';
}
});
这是我为解决问题所做的工作。
$(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;
}
这对我有用
$("body").mouseup(function(e) {
var subject = $(".main-menu");
if(e.target.id != subject.attr('id') && !subject.has(e.target).length) {
$('.sub-menu').hide();
}
});
这是纯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;
})
评论
() => {}
function() {}
如果您只想在单击按钮时显示一个窗口,并在单击外部(或再次单击按钮)时取消显示此窗口,则以下方法很好:
document.body.onclick = function() { undisp_menu(); };
var menu_on = 0;
function menu_trigger(event) {
if (menu_on == 0)
{
// Otherwise you 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;
}
如果您使用的是“弹出窗口”之类的工具,则可以使用“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>
评论
$('#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);
});
我使用了以下通过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, it 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>
您可以在此处阅读教程。
还有焦点
事件:
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>
评论
我只是想让皮斯托斯的答案更加明显,因为它隐藏在评论中。
这个解决方案对我来说非常有效。纯 JavaScript:
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”。
收听正文点击事件:
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”方法。
首先,您必须使用 mouseenter 和 mouseleave 事件跟踪鼠标是在 element1 内部还是外部。 然后,您可以创建一个覆盖整个屏幕的 element2 来检测任何点击,并根据您是在 element1 内部还是外部做出相应的反应。
出于显而易见的原因,我强烈建议同时处理初始化和清理,并且 element2 尽可能临时化。
在下面的示例中,叠加层是位于某处的元素,可以通过单击内部来选择该元素,也可以通过单击外部来取消选择。 _init 和 _release 方法作为自动初始化/清理过程的一部分进行调用。 该类继承自 ClickOverlay,它有一个 inner 和 outerElement,不用担心。我使用了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')
}
}
评论
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>
评论
toggle
active
最简单的方法:mouseleave(function())
更多的信息: https://www.w3schools.com/jquery/jquery_events.asp
评论
2020 解决方案使用原生 JS API 最接近的方法。
document.addEventListener('click', ({ target }) => {
if (!target.closest('#menupop')) {
document.querySelector('#menupop').style.display = 'none'
}
})
#menupop {
width: 300px;
height: 300px;
background-color: red;
}
<div id="menupop">
clicking outside will close this
</div>
评论
还在寻找检测外部咔嗒声的完美解决方案吗?不要再看了!介绍Clickout-Event,一个为clickout和其他类似事件提供通用支持的包,它适用于所有场景:纯HTML属性,vanilla JavaScript,jQuery,Vue.js指令,应有尽有。只要前端框架在内部用于处理事件,Clickout-Event 就适用于它。只需在页面中的任何位置添加脚本标签,它就会像魔术一样工作。onclickout
.addEventListener('clickout')
.on('clickout')
v-on:clickout
addEventListener
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>
所有这些答案都解决了这个问题,但我想贡献一个 moders 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>
这对我来说很好用。我不是专家。
$(document).click(function(event) {
var $target = $(event.target);
if(!$target.closest('#hamburger, a').length &&
$('#hamburger, a').is(":visible")) {
$('nav').slideToggle();
}
});
现在是 2020 年,您可以使用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>
评论
if (el === event.target || el.contains(event.target))
我已经阅读了 2021 年的所有内容,但如果没有错的话,没有人建议像这样简单的事情来解开绑定和删除事件。使用前面的两个答案和更小的技巧,所以我把所有答案都放在一个(它也可以作为参数添加到函数中以传递选择器,以获得更多弹出窗口)。
也许有人知道这个笑话是有用的。也可以这样完成:
<div id="container" style="display:none"><h1>my menu is nice, but it disappears 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>
用于辅助功能focusout
这里有一个答案(非常正确)说,关注事件是一个可访问性问题,因为我们希望迎合键盘用户。该事件是此处使用的正确方法,但它可以比在另一个答案中更简单地完成(在纯 JavaScript 中也是如此):click
focusout
一种更简单的方法:
使用的“问题”是,如果对话框/模态/菜单中的元素失去焦点,而“内部”元素仍然会被触发。我们可以通过查看来检查情况是否如此(这告诉我们哪些元素将获得焦点)。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
评论
这是我找到的这个问题的最简单答案:
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);
你不需要(太多)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>
对于那些想要将简短的解决方案集成到他们的 JavaScript 代码中的人来说,一个没有 jQuery 的小库:
用法:
// Demo code
var htmlElem = document.getElementById('my-element')
function doSomething(){ console.log('outside click') }
// Use the library
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);
};
}
我从这里获取代码并将其放入一个函数中: 如何检测元素外部的点击
这是容器或整个文档中的解决方案。如果点击目标不在元素中(类为“yourClass”),则元素将被隐藏。
$('yourContainer').on('click', function(e) {
if (!$(e.target).hasClass('yourClass')) {
$('.yourClass').hide();
}
});
评论
event.path
event.target
event.stopPropagation