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

Javascript 如何检测元素外部的单击?,javascript,jquery,click,Javascript,Jquery,Click,我有一些HTML菜单,当用户单击这些菜单的头部时,我会完全显示这些菜单。我想在用户在菜单区域外单击时隐藏这些元素 jQuery可以实现类似的功能吗 $("#menuscontainer").clickOutsideThisElement(function() { // Hide the menus }); 检查窗口单击事件目标(它应该传播到窗口,只要它没有在其他任何地方被捕获),并确保它不是任何菜单元素。如果不是,那你就在菜单之外了 或者检查单击的位置,并查看它是否包含在菜单区域内 注

我有一些HTML菜单,当用户单击这些菜单的头部时,我会完全显示这些菜单。我想在用户在菜单区域外单击时隐藏这些元素

jQuery可以实现类似的功能吗

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

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

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

注意:应该避免使用
stopPropagation
,因为它会破坏DOM中的正常事件流。有关更多信息,请参阅。考虑使用,

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

$(窗口)。单击(函数(){
//隐藏菜单(如果可见)
});
$(“#菜单容器”)。单击(函数(事件){
event.stopPropagation();
});

我有一个类似于Eran示例的应用程序,只是在打开菜单时将click事件附加到正文中。。。有点像这样:

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

  event.stopPropagation();
});
var $menuscontainer = ...;

$('#trigger').click(function() {
  $menuscontainer.show();

  $('body').click(function(event) {
    var $target = $(event.target);

    if ($target.parents('#menuscontainer').length == 0) {
      $menuscontainer.hide();
    }
  });
});

有关

的更多信息此处的其他解决方案不适用于我,因此我不得不使用:

if(!$(event.target).is('#foo'))
{
    // hide menu
}
编辑:纯Javascript变体(2021-03-31) 我使用此方法来处理在单击下拉菜单外部时关闭下拉菜单的问题

首先,我为组件的所有元素创建了一个自定义类名。该类名将添加到构成菜单小部件的所有元素中

const className=`dropdown-${Date.now()}-${Math.random()*100}`;
我创建了一个函数来检查单击和单击元素的类名。如果单击的元素不包含我在上面生成的自定义类名,它应该将
show
标志设置为
false
,菜单将关闭

const onClickOutside=(e)=>{
如果(!e.target.className.includes(className)){
show=false;
}
};
然后,我将单击处理程序附加到窗口对象

//加载小部件时添加
window.addEventListener(“单击”,单击外部);
。。。最后是一些家务活

//销毁小部件时删除侦听器
window.removeEventListener(“单击”,再次单击外部);

如果您正在为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;}
this.outsideElement单击=函数(objEvent,objElement){
var objCurrentElement=objEvent.target | | objEvent.src元素;
var blnisidex=假;
var blnSidey=假;
如果(objCurrentElement.getBoundingClientRect().left>=OBJEElement.getBoundingClientRect().left&&objCurrentElement.getBoundingClientRect().right=OBJEElement.getBoundingClientRect().top&&objCurrentElement.getBoundingClientRect().bottom

对我来说很好。

现在有了一个插件:()

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

  • 元素被添加到一个数组中,该数组使用clickoutside处理程序保存所有元素
  • ()单击处理程序绑定到文档(如果还没有)
  • 在文档中单击时,将为该数组中不等于或不属于单击事件目标父级的元素触发clickoutside事件
  • 此外,clickoutside事件的event.target被设置为用户单击的元素(因此您甚至知道用户单击了什么,而不仅仅是他单击了外部)

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

您可以在
文档上侦听单击事件,然后使用确保
#menucontainer
不是被单击元素的祖先或目标

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

$(文档)。单击(函数(事件){
var$target=$(event.target);
如果(!$target.closest('#menucontainer')。长度和
$(“#menucontainer”)是(“:可见”)){
$('#menucontainer').hide();
}        
});
编辑–2017-06-23 如果您计划关闭菜单并停止侦听事件,也可以在事件侦听器之后进行清理。此函数将仅清理新创建的侦听器,保留
文档
上的任何其他单击侦听器。使用ES2015语法:

导出函数HIDEONClickOut(选择器){
const Outside ClickListener=(事件)=>{
const$target=$(event.target);
如果(!$target.closest(selector.length&&$(selector.is)(':visible')){
$(选择器).hide();
removeClickListener();
}
}
常量removeClickListener=()=>{
document.removeEventListener('click',Outside ClickListener)
}
document.addEventListener('click',Outside ClickListener)
}
编辑–2018-03-11 对于那些不想使用jQuery的人,下面是上面的代码,使用的是普通的vanillaJS(ECMAScript 6)

函数隐藏单击外部(元素){
const Outside ClickListener=事件=>{
如果(!element.contains(event.target)&&isVisible(element)){//或使用:event.target.closest(selector)==null
element.style.display='none'
removeClickListener()
}
}
常量removeClickListener=()=>{
document.removeEventListener('click',Outside ClickListener)
}
document.addEventListener('click',Outside ClickListener)
}
const isVisible=elem=>!!elem&&!!(elem.offsetWidth | | elem.offsetHeight | | elem.getclientracts().length)//来源(2018-03-11):https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js 
注意: 这是基于Alex注释,仅使用
!element.contains(event.tar
      ...
      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);
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()
        }
    }
var $menuscontainer = ...;

$('#trigger').click(function() {
  $menuscontainer.show();

  $('body').click(function(event) {
    var $target = $(event.target);

    if ($target.parents('#menuscontainer').length == 0) {
      $menuscontainer.hide();
    }
  });
});
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();
});
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;
});
$('#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);
        }

    })
});
$(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
    }
});
$(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");
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();
    }
});
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";
    }
});
$(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
    }
});
(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);
})();
<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>
$(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();
    }
});
element.addEventListener('blur', ..., true);
//                       use capture: ^^^^
window.addEventListener('mouseup', e => {
        if (e.target != yourDiv && e.target.parentNode != yourDiv) {
            yourDiv.classList.remove('show-menu');
            //or yourDiv.style.display = 'none';
        }
    })
window.addEventListener('mouseup', function (e) {
if (e.target != yourDiv && e.target.parentNode != yourDiv) {
    yourDiv.classList.remove('show-menu'); 
    //or yourDiv.style.display = 'none';
}
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;
})
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
    }
})
<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>