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


2485

我有一些HTML菜单,当用户单击这些菜单的标题时,它们会完整显示。当用户在菜单区域之外单击时,我想隐藏这些元素。

jQuery可能会发生这种情况吗?

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

47
以下是此策略的示例:jsfiddle.net/tedp/aL7Xe/1
Ted

18
正如Tom所提到的,在使用这种方法之前,您需要阅读css-tricks.com/dangers-stopping-event-propagation。那个jsfiddle工具非常酷。
乔恩·库姆斯

3
得到元素的引用,然后event.target,最后=或==两者然后相应地执行代码..!
罗希特·库马尔


2
带有event.target 带有的Vanilla JS解决方案event.stopPropagation
lowtechsun

Answers:


1811

注意:stopEventPropagation()应该避免使用,因为它会破坏DOM中的正常事件流。有关更多信息,请参见本文。考虑使用这种方法,而不是

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

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

$('#menucontainer').click(function(event){
    event.stopPropagation();
});

708
这破坏了#menucontainer中包含的许多内容的标准行为,包括按钮和链接。我很惊讶这个答案如此受欢迎。
艺术

75
这不会破坏#menucontainer中任何内容的行为,因为它在其中任何内容的传播链的底部。
伊兰·加珀林

94
它非常美丽,但您不应该使用$('html').click()身体。身体始终具有其内容的高度。它内容不多或屏幕非常高,仅适用于身体填充的部分。
meo

103
我也很惊讶这个解决方案获得了如此多的选票。对于任何外部具有stopPropagation jsfiddle.net/Flandre/vaNFw/3的
Andre

140
菲利普·沃尔顿(Philip Walton)很好地解释了为什么这个答案不是最佳解决方案:css-tricks.com/dangers-stopping-event-propagation
汤姆(Tom)

1386

您可以使用侦听click事件document,然后确保#menucontainer它不是其祖先或被单击元素的目标 .closest()

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

$(document).click(function(event) { 
  $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) => {
    $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的人。这是上面的纯香草代码(ECMAScript6)中的代码。

function hideOnClickOutside(element) {
    const outsideClickListener = event => {
        if (!element.contains(event.target) && isVisible(element)) { // or use: event.target.closest(selector) === null
          element.style.display = 'none'
          removeClickListener()
        }
    }

    const removeClickListener = () => {
        document.removeEventListener('click', outsideClickListener)
    }

    document.addEventListener('click', outsideClickListener)
}

const isVisible = elem => !!elem && !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ) // source (2018-03-11): https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js 

注意: 这是基于Alex注释,仅用于!element.contains(event.target)代替jQuery部分。

element.closest()现在在所有主流浏览器中都可用(W3C版本与jQuery版本略有不同)。可以在这里找到Polyfills: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()
        }
    }

28
我尝试了许多其他答案,但只有一个可行。谢谢。我最终使用的代码是:$(document).click(function(event){if($(event.target).closest('。window')。length == 0){$('。window' ).fadeOut('fast');}});
Pistos'4

39
实际上,我最终使用了该解决方案,因为它更好地支持同一页面上的多个菜单,其中在第一个菜单处于打开状态时单击第二个菜单将使stopPropagation解决方案中的第一个菜单保持打开状态。
umassthrower 2012年

13
极好的答案。当您有多个要关闭的项目时,这是一种方法。
约翰,

5
由于其他解决方案在event.stopPropagation()中存在缺陷,因此应该将其作为可接受的答案。
番茄

20
没有jQuery的 - !element.contains(event.target)Node.contains()
亚历克斯·罗斯

303

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

这个问题之所以如此流行并且答案如此之多,是因为它看似复杂。经过将近八年的时间和数十个答案,我真的很惊讶地看到对可访问性的关注很少。

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

这是一个崇高的原因,是实际的问题。问题的标题(即大多数答案似乎试图解决的问题)包含不幸的鲱鱼。

提示:这是“点击”一词!

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

如果要绑定单击处理程序以关闭对话框,则您已经失败了。您失败的原因是,并非所有人都触发click事件。不使用鼠标的用户将可以通过按来退出对话框(并且弹出菜单可以说是对话框的一种)Tab,然后他们将无法在不触发click事件的情况下读取对话框后面的内容。

因此,让我们改一下这个问题。

用户完成操作后如何关闭对话框?

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

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

focusout 事件

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

提示:请注意blur事件,blur如果事件绑定到冒泡阶段,则不会传播!

jQuery的效果focusout很好。如果您不能使用jQuery,则可以blur在捕获阶段使用:

element.addEventListener('blur', ..., true);
//                       use capture: ^^^^

同样,对于许多对话框,您将需要允许容器获得焦点。添加tabindex="-1"以允许对话框动态接收焦点,而不会中断制表流程。

$('a').on('click', function () {
  $(this.hash).toggleClass('active').focus();
});

$('div').on('focusout', function () {
  $(this).removeClass('active');
});
div {
  display: none;
}
.active {
  display: block;
}
<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>


如果您在演示中玩了超过一分钟,则应该很快开始发现问题。

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

解决方法是在状态循环上排队状态更改。这可以通过使用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({
  focusout: function () {
    $(this.hash).data('timer', setTimeout(function () {
      $(this.hash).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this.hash).data('timer'));  
  }
});


Esc

如果您认为已经通过处理焦点状态完成了操作,则可以做更多的事情来简化用户体验。

这通常是一个“很不错”的功能,但是很常见的是,当您拥有任何形式的模式或弹出窗口时,Esc键会将其关闭。

keydown: function (e) {
  if (e.which === 27) {
    $(this).removeClass('active');
    e.preventDefault();
  }
}


如果知道对话框中有可聚焦的元素,则无需直接聚焦对话框。如果要构建菜单,则可以将焦点放在第一个菜单项上。

click: function (e) {
  $(this.hash)
    .toggleClass('submenu--active')
    .find('a:first')
    .focus();
  e.preventDefault();
}


WAI-ARIA角色和其他辅助功能支持

这个答案有望涵盖此功能可访问的键盘和鼠标支持的基础知识,但是由于它已经相当大了,因此我将避免任何有关WAI-ARIA角色和属性的讨论,但是我强烈建议实现者参考该规范以获取详细信息。他们应该使用什么角色以及任何其他适当的属性。


29
这是最完整的答案,请牢记解释和可访问性。我认为这应该是可以接受的答案,因为大多数其他答案只能处理点击,只是删除了代码段而没有任何解释。
西里尔(Cyrille)

4
太神奇了,很好解释。我只是在React组件上使用了这种方法,所以工作非常完美
David Lavieri

2
@zzzzBov感谢您的深入回答,我正在尝试在香草JS中实现它,而我对所有jquery东西都有些迷失。香草js中有类似的东西吗?
HendrikEng

2
@zzzzBov不,我当然不是要您编写无jQuery版本,我会尽力做到这一点,我想最好的办法是在这里问一个新问题,如果我真的被卡住了。再次非常感谢。
HendrikEng

7
尽管这是检测自定义下拉菜单或其他输入中单击的好方法,但出于两个原因,它绝不应该用作模态或弹出窗口的首选方法。当用户切换到另一个选项卡或窗口或打开上下文菜单时,该模式将关闭,这确实令人讨厌。此外,鼠标单击时会触发“点击”事件,而“聚焦”事件会触发您按下鼠标的瞬间。通常,仅当您按下鼠标按钮然后放开时,按钮才会执行操作。对模态执行此操作的正确且可访问的方法是添加一个可选项的关闭按钮。
凯文

144

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

if(!$(event.target).is('#foo'))
{
    // hide menu
}

我已经发布了另一个实际示例,说明如何使用event.target避免将其他Jquery UI小部件嵌入自己的弹出框时触发其他外部单击html处理程序:获得原始目标的最佳方法
Joey T

43
这对我有用,除了我&& !$(event.target).parents("#foo").is("#foo")IF语句中添加了内容,以便在单击任何子元素时都不会关闭菜单。
honyovk 2012年

1
找不到比以下更好的东西://我们不在$(event.target).parents('#foo')。length == 0
AlexG 2013年

3
使用可以处理深层嵌套的简洁改进.is('#foo, #foo *'),但是我不建议绑定单击处理程序解决这个问题
zzzzBov

1
!$(event.target).closest("#foo").length会更好,并且不需要添加@honyovk。
tvanc

127

我有一个与Eran的示例类似的应用程序,除了打开菜单时将click事件附加到主体上。

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

  event.stopPropagation();
});

有关jQuery 函数的更多信息one()


9
但是,如果您单击菜单本身,然后在菜单外部,它将不起作用:)
vsync

3
在将点击侦听器绑定到正文之前,将event.stopProgagantion()放置有助于解决问题。
贾斯珀·肯尼斯

4
问题是“一个”适用于将事件多次添加到数组的jQuery方法。因此,如果您单击菜单多次以将其打开,则事件将再次绑定到主体,并尝试多次隐藏菜单。应该应用故障安全解决此问题。
marksyzm

在处理程序.one内部使用- 绑定后,$('html')编写一个$('html').off('click')
科迪

4
@Cody我认为这没有帮助。该one处理器会自动调用off(因为它在jQuery的文档显示真实)。
马里亚诺·德桑兹

44

经过研究,我找到了三种可行的解决方案(我忘记了页面链接以供参考)

第一个解决方案

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

8
第三种解决方案是迄今为止最优雅的检查方法。它还不涉及jQuery的任何开销。非常好。这很有帮助。谢谢。
dbarth '16

我正在尝试将第三个解决方案与多个元素一起使用document.getElementsByClassName,如果有人有线索请分享。
lowtechsun

@lowtechsun您必须循环检查每个。
Donnie D'Amato

真的很喜欢第三个解决方案,但是单击会在我的div开始显示之前触发,从而再次隐藏了它,你知道为什么吗?
indiehjaerta

当然,第三个允许console.log,但是它不允许您通过将display设置为none来关闭它,因为它会在显示菜单之前执行此操作。您对此有解决方案吗?
RolandiXor '18 -10-10

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

对我有用。


3
这是我要用的那个。它可能不是完美的,但是作为一个爱好程序员,它的简单程度足以使其清楚地理解。
kevtrout 2010年

模糊是外面的举动#menucontainer,问题是关于点击的问题
borrel

4
@borrel模糊不是容器外部的动作。模糊与焦点相反,您正在考虑将鼠标移出。当我创建“单击以编辑”文本时,我在纯文本和单击的输入字段之间来回切换时,此解决方案对我来说特别有效。
parker.sikand

我必须添加tabindex="-1"#menuscontainer使其工作。看来,如果将输入标签放在容器中并单击它,则容器将被隐藏。
tyrion 2014年

mouseleave事件更适合菜单和容器(参考:w3schools.com/jquery/…
Evgenia Manolova 2015年

38

现在有一个用于此的插件:外部事件博客文章

clickoutside处理程序(WLOG)绑定到元素时,将发生以下情况:

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

因此,不会阻止任何事件的传播,并且可以在外部处理程序的“上方”使用其他单击处理程序。


不错的插件。“外部事件”上的链接已失效,而博客文章链接仍处于活动状态,并且为“ clickoutside”类事件提供了一个非常有用的插件。它也是麻省理工学院许可的。
TechNyquist'4

很棒的插件。工作完美。用法如下:$( '#element' ).on( 'clickoutside', function( e ) { .. } );
Gavin

33

这完全适合我!

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

此解决方案非常适合我的工作,在这里我仍然需要单击事件来冒泡,谢谢。
Mike

26

我不认为您真正需要的是在用户单击外部时关闭菜单。您需要的是,当用户单击页面上的任意位置时,菜单关闭。如果单击菜单或关闭菜单,它应该关闭吗?

上面找不到满意的答案,促使我前几天写了这篇博客。对于更加学究的人来说,有许多陷阱需要注意:

  1. 如果在单击时将click事件处理程序附加到body元素,请确保在关闭菜单并取消绑定事件之前等待第二次单击。否则,打开菜单的click事件将冒泡到必须关闭菜单的侦听器中。
  2. 如果在click事件上使用event.stopPropogation(),则页面中的其他任何元素都不能具有“单击任何位置关闭”功能。
  3. 将click事件处理程序无限期地附加到body元素上并不是一种有效的解决方案
  4. 比较事件的目标及其处理程序的创建者的父级,假定您想要的是单击菜单时关闭菜单,而您真正想要的是单击页面上的任意位置时关闭菜单。
  5. 在body元素上侦听事件将使您的代码更加脆弱。像这样天真无邪的样式会破坏它:body { margin-left:auto; margin-right: auto; width:960px;}

2
“如果您单击菜单,或者关闭菜单,它应该关闭对吗?” 不总是。通过拖动元素取消单击仍会触发文档级别的单击,但其目的不是继续关闭菜单。还有许多其他类型的对话框可以使用“单击”行为,允许在内部单击。
zzzzBov

25

正如另一位张贴者所说的那样,有很多陷阱,特别是如果要显示的元素(在本例中为菜单)具有交互式元素。我发现以下方法相当可靠:

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

    })
});

25

一个简单的解决方案是:

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

上面的脚本将隐藏click事件div外部div是否被触发。

您可以查看以下博客以获取更多信息:http : //www.codecanal.com/detect-click-outside-div-using-javascript/


22

解决方案1

与其使用可能会有一些副作用的event.stopPropagation(),不如定义一个简单的flag变量并添加一个if条件。我对此进行了测试,并且正常工作,没有stopPropagation的任何副作用:

var flag = "1";
$('#menucontainer').click(function(event){
    flag = "0"; // flag 0 means click happened in the area where we should not do any action
});

$('html').click(function() {
    if(flag != "0"){
        // Hide the menus if visible
    }
    else {
        flag = "1";
    }
});

解决方案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
    }
});

我将此解决方案与布尔值标志一起使用,并且对于铰接的DOm也是很好的,并且如果在#menucontainer内还有很多其他元素
Migio B

解决方案1效果更好,因为它可以处理在事件传播到文档时从DOM中删除单击目标的情况。
爱丽丝

21

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

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


18

我在以下方面取得了成功:

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仅在(单击的目标)不是它的子级时才隐藏的主体上。


17

作为变体:

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解决方案中的第一个菜单保持打开状态。


16

该事件具有元素的名为event.path的属性,该属性是“所有祖先以树顺序排列的静态有序列表”。要检查事件是否源自特定的DOM元素或其子元素之一,只需检查该特定DOM元素的路径即可。通过ORsome功能上对元素检查进行逻辑检查,还可以用于检查多个元素。

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

1
在Edge中不起作用。
传奇

event.path不是一回事。
安德鲁

14

我在某些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);

13

这是面向未来观众的原始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);
})();

如果您将在同一页面上进行多个切换,则可以使用以下方法:

  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>


13

令我惊讶的是,没有人真正意识到这一focusout事件:

var button = document.getElementById('button');
button.addEventListener('click', function(e){
  e.target.style.backgroundColor = 'green';
});
button.addEventListener('focusout', function(e){
  e.target.style.backgroundColor = '';
});
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
</head>
<body>
  <button id="button">Click</button>
</body>
</html>


我想你错过了我的答案。stackoverflow.com/a/47755925/6478359
Muhammet Can TONBUL

这应该是公认的答案。谢谢!!!
幸运林

8

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

8

与其使用流程中断,模糊/聚焦事件或任何其他棘手的技术,只需将事件流程与元素的亲属关系相匹配:

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

对我来说,这是最好的解决方案。在动画后的回调中使用它,因此我需要进行detach事件。+1
qwertzman 2014年

这里有多余的支票。如果该元素确实是#menuscontainer您仍然在经历它的父母。您应该先检查一下,如果不是那个元素,那么就去DOM树。
vsync 2014年

对 !您可以将条件更改为if(!($(event.target).is("#menuscontainer") || $(event.target).parents().is("#menuscontainer"))){。这是一个很小的优化,但在程序生命周期中仅发生几次:对于每次单击,如果click.menu-outside事件已注册。它更长(+32个字符)并且不使用方法链接
mems

8

采用:

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

7

如果有人好奇这里是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';
}

});


7

这是纯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;
})

“ ES6最新”是一个相当大胆的主张,当ES6最新的唯一事情是() => {}代替function() {}。您所拥有的归类为带有ES6的纯JavaScript。
MortenMoulder

@MortenMoulder:是的。即使实际上是ES6,也只是为了引起注意。但是,请看解决方案。我觉得很好。
Duannx '17

它是普通的JS,适用于从DOM中删除的事件目标(例如,当选择了内部弹出窗口的值时,立即关闭弹出窗口)。向我+1!
爱丽丝

6

将单击事件侦听器挂在文档上。在事件侦听器内部,您可以查看事件对象,特别是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");
    }
});

6

我已经使用下面的脚本并完成了jQuery。

jQuery(document).click(function(e) {
    var target = e.target; //target div recorded
    if (!jQuery(target).is('#tobehide') ) {
        jQuery(this).fadeOut(); //if the click element is not the above id will hide
    }
})

在下面找到HTML代码

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

您可以在这里阅读教程


5
$(document).click(function() {
    $(".overlay-window").hide();
});
$(".overlay-window").click(function() {
    return false;
});

如果单击文档,则隐藏给定元素,除非您单击相同的元素。


5

支持最受欢迎的答案,但请添加

&& (e.target != $('html').get(0)) // ignore the scrollbar

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


5

为了更易于使用和更具表现力的代码,我为此创建了一个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...
});
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.