如何检测元素外部的单击?

我有一些超文本标记语言菜单,当用户点击这些菜单的头部时,我完全显示出来。当用户点击菜单区域之外时,我想隐藏这些元素。

这样的事情在jQuery中可能发生吗?

$("#menuscontainer").clickOutsideThisElement(function() {// Hide the menus});
1611546 次浏览

检查窗口单击事件目标(它应该传播到窗口,只要它没有在其他任何地方被捕获),并确保它不是任何菜单元素。如果不是,那么你在菜单之外。

或者检查单击的位置,看看它是否包含在菜单区域中。

注意:应该避免使用stopPropagation,因为它会破坏DOM中的正常事件流。有关更多信息,请参阅这篇CSS技巧文章。考虑改用这种方法

将单击事件附加到文档正文以关闭窗口。将单独的单击事件附加到容器以停止传播到文档正文。

$(window).click(function() {//Hide the menus if visible});
$('#menucontainer').click(function(event){event.stopPropagation();});

我有一个类似于Eran示例的应用程序,除了我在打开菜单时将单击事件附加到主体上……有点像这样:

$('#menucontainer').click(function(event) {$('html').one('click',function() {// Hide the menus});
event.stopPropagation();});

更多信息jQuery的#0函数

这里的其他解决方案对我不起作用,所以我不得不使用:

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;}};

然后我将单击处理程序附加到窗口对象。

// add when widget loadswindow.addEventListener("click", onClickOutside);

…最后是一些家务活

// remove listener when destroying the widgetwindow.removeEventListener("click", onClickOutside);

如果您正在为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;elsereturn true;}

$("#menuscontainer").click(function() {$(this).focus();});$("#menuscontainer").blur(function(){$(this).hide();});

为我工作就好了。

现在有一个插件:外部活动博客文章

外部点击处理程序(WLOG)绑定到元素时会发生以下情况:

  • 该元素被添加到一个数组中,该数组包含具有外部点击处理程序的所有元素
  • 命名空间点击处理程序绑定到文档(如果尚未存在)
  • 在文档中的任何点击上,都会为该数组中不等于点击事件目标或其父级的元素触发外部点击事件
  • 此外,外部点击事件的event.target设置为用户单击的元素(因此您甚至知道用户单击了什么,而不仅仅是他单击了外部)

因此,不会停止传播任何事件,并且可以使用外部处理程序在元素的“上方”使用额外的点击处理程序。

这对我来说很及时:

$('body').click(function() {// Hide the menus if visible.});

您可以监听document上的点击事件,然后使用#2确保#menucontainer不是单击元素的祖先或目标。

如果不是,则单击的元素位于#menucontainer之外,您可以安全地隐藏它。

$(document).click(function(event) {var $target = $(event.target);if(!$target.closest('#menucontainer').length &&$('#menucontainer').is(":visible")) {$('#menucontainer').hide();}});

编辑-2017-06-23

如果您计划关闭菜单并希望停止监听事件,您也可以在事件监听器之后进行清理。此函数将仅清理新创建的监听器,保留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);}

编辑-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) === nullelement.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版本略有不同)。可以在这里找到: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) === nullelement.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();});

我不认为你真正需要的是当用户点击外部时关闭菜单;你需要的是当用户点击页面上的任何地方时菜单关闭。如果你点击菜单,或者关闭菜单,它应该关闭对吗?

在上面找不到令人满意的答案促使我前几天写了这篇博客文章。为了更迂腐,有一些陷阱需要注意:

  1. 如果您在单击时将单击事件处理程序附加到body元素,请确保在关闭菜单之前等待第二次单击并解除绑定事件。否则打开菜单的单击事件将冒泡到必须关闭菜单的侦听器。
  2. 如果您在单击事件上使用event.stopPropoation(),则页面中的任何其他元素都不能具有单击任意关闭功能。
  3. 无限期地将单击事件处理程序附加到body元素不是一个高性能的解决方案
  4. 将事件的目标及其父级与处理程序的创建者进行比较,假设您想要的是在单击菜单时关闭它,而您真正想要的是在单击页面上的任何位置时关闭它。
  5. 监听body元素上的事件会使你的代码更加脆弱。

我在一些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 menuif ($(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 hideif ( $('#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 #menuscontainerconsole.log("hide");}});

这对我来说是完美的!!!

$('html').click(function (e) {if (e.target.id == 'YOUR-DIV-ID') {//do something} else {//do something}});

说实话,我不喜欢以前的任何解决方案。

做到这一点的最佳方法是将“单击”事件绑定到文档,并比较该单击是否真的在元素之外(就像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 bodyif ($(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 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);

最广泛的方法是选择网页上的所有内容,除了您不希望检测到点击的元素,并在菜单打开时绑定单击事件。

然后当菜单关闭时,删除绑定。

使用.停止传播,以防止事件影响菜单容器的任何部分。

$("*").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.stop传播()和所有其他技巧将不起作用。

我想出了一个解决方案,也许不是那么容易,但总比没有好。看看:

$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 treeif(!$(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");

标记为已接受答案的答案没有考虑到您可以覆盖元素,例如对话框、popovers、datepickers等。单击这些不应隐藏元素。

我已经制作了自己的版本,它确实考虑到了这一点。它是作为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;}}};})();

这是一个更通用的解决方案,允许监视多个元素,并从队列中动态添加和删除元素

它包含一个全局队列(自动关闭队列)——一个对象容器,用于在外部单击时应该关闭的元素。

每个队列对象键应该是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 elementconsole.log('This is a click outside the element with id: ' + id);if (typeof element.onOutsidePress == 'function') element.onOutsidePress(event, id); //call the outside callbackdelete autoCloseQueue[id]; //remove the element from the queue}}});

然后,当创建id为“菜单容器”的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目的。为了不通过,我不得不使用返回false。当然,在那里有一个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>

标准超文本标记语言:

<label>包围菜单并获取焦点状态更改。

另外:您可以通过Tab展开菜单。

作为包装这个伟大的答案从艺术,并使用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 itif ($menu.is(':visible') && !$menu.is(e.target) && !$menu.has(e.target).length) {$menu.hide();}});

它对停止事件传播没有问题,并且更好地支持同一页面上的多个菜单,其中在打开第一个菜单时单击第二个菜单将使第一个菜单在停止传播解决方案中打开。

对于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');});
$("body > div:not(#dvid)").click(function (e) {//your code});
$("html").click(function(){if($('#info').css("opacity")>0.9) {$('#info').fadeOut('fast');}});

解决方案1

而不是使用event.stop传播(),它可能会有一些副作用,只需定义一个简单的标志变量并添加一个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>

如果你要在同一个页面上有多个切换,你可以使用这样的东西:

  1. 将类名hidden添加到可折叠项。
  2. 单击文档后,关闭所有不包含单击元素且未隐藏的隐藏元素
  3. 如果单击的元素是切换,则切换指定的元素。

(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 ICONif ($("#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

因此,单击滚动条不会[隐藏或任何]您的目标元素。

这是调整超文本标记语言是更好的解决方案的典型案例。为什么不在不包含菜单项的元素上设置单击?这样您就不需要添加传播。

$('.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 setvar hasExclusions = typeof exclusions !== 'undefined';
//switches click event with touch event if on a touch devicevar ClickOrTouchEvent = "ontouchend" in document ? "touchend" : "click";
$('body').on(ClickOrTouchEvent, function(event) {//click target does not contain subject as a parentvar clickedOutside = !$(event.target).closest(subject).length;
//click target was on one of the excluded elementsvar 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

经过研究,我找到了三个工作解决方案(我忘记了页面链接以供参考)

第一个解决方案

<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 sectioncontainer.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/

$('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();}});

但请注意,如果单击事件无法到达html标记,这将无法工作。(也许其他元素有stopPropagation())。

订阅捕获阶段的Click以处理调用preventDefault的元素的单击。
使用另一个名称click-anywhere在文档元素上重新启动它。

document.addEventListener('click', function (event) {event = $.event.fix(event);event.type = 'click-anywhere';$document.trigger(event);}, true);

然后,您需要单击外部功能订阅document上的click-anywhere事件并检查单击是否在您感兴趣的元素之外:

$(document).on('click-anywhere', function (event) {if (!$(event.target).closest('#smth').length) {// Do anything you need here}});

一些备注:

  • 您必须使用document,因为它将是一个性能错误,以触发发生单击的所有元素上的事件。

  • 此功能可以包装到特殊插件中,该插件在外部单击时调用一些回调。

  • 您不能使用jQuery本身订阅捕获阶段。

  • 你不需要文档加载来订阅,因为订阅在document上,即使不在它的body上,所以它总是独立地存在。

为了更容易使用和更具表现力的代码,我为此创建了一个jQuery插件:

$('div.my-element').clickOut(function(target) {//do something here...});

注意:目标是用户实际单击的元素。但是回调仍然在原始元素的上下文中执行,因此您可以按照预期在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、输出

如何检测元素外部的单击?

这个问题如此受欢迎并有如此多答案的原因是它具有欺骗性的复杂性。在将近八年和几十个答案之后,我真的很惊讶地看到对可访问性的关注如此之少。

当用户在菜单区域外单击时,我想隐藏这些元素。

这是一项崇高的事业,是0号问题。问题的标题——这是大多数答案似乎试图解决的问题——包含了一个不幸的红鲱鱼。

提示:这是单词“点击”

您实际上不想绑定单击处理程序。

如果您正在绑定单击处理程序以关闭对话框,那么您已经失败了。您失败的原因是不是每个人都会触发click事件。不使用鼠标的用户将能够通过按Tab来转义您的对话框(您的弹出菜单可以说是一种对话框),然后他们将无法在不触发click事件的情况下读取对话框后面的内容。

让我们换个说法。

当用户完成对话框时,如何关闭对话框?

这就是目标。不幸的是,现在我们需要绑定userisfinishedwiththedialog事件,而这种绑定并不那么简单。

那么我们如何才能检测到用户已经完成了对话框的使用呢?

focusout事件

一个好的开始是确定焦点是否离开了对话框。

提示:小心blur事件,如果事件绑定到冒泡阶段,blur不会传播!

jQuery的#0就可以了。如果你不能使用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>


如果您使用该演示超过一分钟,您应该很快开始看到问题。

首先是对话框中的链接不可点击。尝试单击它或选项卡到它将导致对话框在交互发生之前关闭。这是因为聚焦内部元素会在再次触发focusin事件之前触发focusout事件。

修复方法是在事件循环上对状态更改进行排队。这可以通过使用setImmediate(...)来完成,或者对于不支持setImmediate的浏览器使用setTimeout(..., 0)。一旦排队,它可以被后续的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角色和属性的讨论,但是我建议实施者参考规范以了解他们应该使用的角色和任何其他适当属性的详细信息。

隐藏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动态添加的菜单。基本上,它只允许您单击文档中的任何位置,并检查您单击的元素,并选择它最接近的“#menus容器”。然后它隐藏所有菜单容器,但不包括您单击的那个。

不确定您的菜单是如何构建的,但请随时在JSFiddle中复制我的代码。这是一个非常简单但功能齐全的菜单/模态系统。您需要做的就是构建html菜单,代码将为您完成工作。

https://jsfiddle.net/zs6anrn7/

我知道这个问题有一百万个答案,但我一直喜欢使用超文本标记语言和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 hereif ($(".element").hasClass("active")){$(".element").removeClass("active");}}});

尝试此代码以获得解决方案。

该事件有一个名为元素event.path的属性,它是msgstr"所有祖先的静态有序列表以树的顺序排列"。要检查事件是否源自特定的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 isMenuClickisMenuClick = false;})menu.addEventListener('click',()=>{isMenuClick = true;})

如果您只是想在单击按钮时显示一个窗口,并在单击外部(或再次单击按钮)时取消显示此窗口,则此波纹管工作良好

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 buttonevent.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;}

如果您使用的是“Pop-up”之类的工具,则可以使用“onFocus usOut”事件。

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 recordedif (!jQuery(target).is('#tobehide') ) {jQuery(this).fadeOut(); //if the click element is not the above id will hide}})

下面找到超文本标记语言代码

<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>

您可以阅读教程这里

我很惊讶没有人真正承认#0事件:

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>

我只是想让@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 == 0elementToToggle.hide()

假设您要检测用户在外部还是内部单击的div有一个id,例如:“my-特殊部件”。

监听主体点击事件:

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;}

在这种情况下,您不会破坏页面中某些元素的正常点击流程,因为您没有使用“停止传播”方法。

首先,您必须使用mouseenter和Mouseleave事件跟踪鼠标在元素内部或外部1。然后你可以创建一个覆盖整个屏幕的element2来检测任何点击,并根据你在element1内部或外部做出相应的反应。

我强烈建议同时处理初始化和清理,并且由于显而易见的原因,元素2尽可能临时。

在下面的示例中,覆盖是位于某处的元素,可以通过单击内部来选择,通过单击外部来取消选择。_init和_release方法作为自动初始化/清理过程的一部分被调用。该类继承自ClickOverlay,它有一个内部和外部元素,别担心。我使用outerElement.parentNode.append孩子来避免冲突。

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)returnthis.unselectHelper = document.createElement('div')this.unselectHelper.classList.add('unselect-helper')this.unselectListener = () => {if (this.mouseInside)returnthis.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)returnthis.unselectHelper.removeEventListener('pointerdown', this.unselectListener)this.unselectHelper.remove()delete this.unselectListenerdelete 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>

最简单的方法: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>

还在寻找检测外部点击的完美解决方案吗?不要再找了!介绍点击事件,一个为点击和其他类似事件提供通用支持的包,它适用于所有种场景:纯超文本标记语言onclickout属性,.addEventListener('clickout')的香草JavaScript,.on('clickout')的jQuery,v-on:clickout的Vue.js指令,你的名字。只要前端框架在内部使用addEventListener来处理事件,Clickout-Event就可以为它工作。只需在页面的任何地方添加脚本标签,它就像魔术一样工作。

超文本标记语言属性

<div onclickout="console.log('clickout detected')">...</div>

Vanilla JavaScript

document.getElementById('myId').addEventListener('clickout', myListener);

jQuery

$('#myId').on('clickout', myListener);

Vue.js

<div v-on:clickout="open=false">...</div>

Angular

<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()

来自:https://developer.mozilla.org/en-US/docs/Web/API/Event/composedPath

Event接口的复合路径()方法返回事件的路径,该路径是将调用侦听器的对象数组。

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>

我已经读了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>

欢呼

使用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 closedialog.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 menusmenu.style.display = "none";}, false);
document.getElementById("menuscontainer").addEventListener("click", function(e){// Show the menusmenu.style.display = "block";e.stopPropagation();}, false);

你不需要(很多)JavaScript,只需要:focus-within选择器:

  • 使用.sidebar:focus-within显示您的侧边栏。
  • 在侧边栏和body元素上设置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 codevar htmlElem = document.getElementById('my-element')function doSomething(){ console.log('outside click') }
// use the libvar removeListener = new elemOutsideClickListener(htmlElem, doSomething);
// deregister on your wished event$scope.$on('$destroy', removeListener);

下面是lib:

function elemOutsideClickListener (element, outsideClickFunc, insideClickFunc) {function onClickOutside (e) {var targetEl = e.target; // clicked elementdo {// click insideif (targetEl === element) {if (insideClickFunc) insideClickFunc();return;
// Go up the DOM} else {targetEl = targetEl.parentNode;}} while (targetEl);
// click outsideif (!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