8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

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

HamZa Samha 1月前

62 0

我有一些 HTML 菜单,当用户点击这些菜单的顶部时,这些菜单会完全显示出来。当用户点击菜单区域之外的区域时,我想隐藏这些元素。就像这样...

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

使用 jQuery 可以实现这样的效果吗?

$("#menuscontainer").clickOutsideThisElement(function() {
    // Hide the menus
});
帖子版权声明 1、本帖标题:如何检测元素外部的点击?
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由HamZa Samha在本站《css》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 获取对元素的引用,然后获取 event.target,最后 != 或 == 它们两者,然后相应地执行代码。

  • 注意: stopPropagation 应避免使用,因为它会破坏 DOM 中的正常事件流。 有关详细信息, 此 CSS Tricks 文章 此方法

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

    $(window).click(function() {
      //Hide the menus if visible
    });
    
    $('#menucontainer').click(function(event){
      event.stopPropagation();
    });
    
  • 这破坏了 #menucontainer 中包含的许多东西(包括按钮和链接)的标准行为。我很惊讶这个答案如此受欢迎。

  • 这不会破坏 #menucontainer 内部任何东西的行为,因为它位于其内部任何东西的传播链的底部。

  • 它非常漂亮,但你应该使用 $('html').click() 而不是 body。body 的高度始终等于其内容的高度。如果内容不多或屏幕很高,它只对 body 填充的部分起作用。

  • 我也惊讶于这个解决方案得到了这么多的投票。对于任何具有 stopPropagation 的元素,此方法都会失败 jsfiddle.net/Flandre/vaNFw/3

  • Philip Walton 很好地解释了为什么这个答案不是最好的解决方案:css-tricks.com/dangers-stopping-event-propagation

  • 您可以监听 点击 事件 document 它不是被点击元素的祖先或目标 #menucontainer 确保 .closest() .

    如果不是,那么被点击的元素就在外面 #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) === 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()
            }
        }
    
  • 我尝试了许多其他答案,但只有这一个有效。谢谢。我最终使用的代码是:$(document).click( function(event) { if( $(event.target).closest('.window').length == 0 ) { $('.window').fadeOut('fast'); } } );

  • 我最终选择了这个解决方案,因为它更好地支持同一页面上的多个菜单,当第一个菜单打开时单击第二个菜单将使第一个菜单在 stopPropagation 解决方案中保持打开状态。

  • 引用 12

    不使用 jQuery - !element.contains(event.target) 使用 Node.contains()

  • 如果您正在阅读本文,那么您应该查看一些比该答案更具可读性的更现代的答案来解决这个问题。

  • KDW 1月前 0 只看Ta
    引用 14

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

    这个问题之所以如此受欢迎,并有如此多的答案,是因为它看似复杂。经过近八年的时间和数十个答案,我真的很惊讶地发现人们对可访问性的关注如此之少。

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

    这是一项崇高的事业,也是 真正的 问题。问题的标题——大多数答案似乎都试图解决这个问题——包含了一个不幸的干扰性话题。

    提示:它是单词“click”!

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

    如果您绑定了点击处理程序来关闭对话框,那么您已经失败了。失败的原因是,并非每个人都会触发 click Tab退出对话框(您的弹出菜单可以说是一种对话框),然后他们将无法阅读对话框后面的内容,除非随后触发事件 click

    因此让我们重新表述一下这个问题。

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

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

    那么我们如何检测用户已经结束使用对话框呢?

    focusout 事件

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

    提示:小心处理模糊事件,如果事件绑定到冒泡阶段,模糊就不会传播!

    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>

    如果您玩该演示超过一分钟,您很快就会开始发现问题。

    首先,对话框中的链接不可点击。尝试点击它或按 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>

    第二个问题是,再次按下链接时对话框不会关闭。这是因为对话框失去焦点,触发关闭行为,之后单击链接会触发对话框重新打开。

    与上一个问题类似,需要管理焦点状态。鉴于状态更改已排队,因此只需处理对话框触发器上的焦点事件即可:

    This should look familiar
    $('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 角色和属性 ,但我 强烈 建议实施者参考规范以了解他们应该使用什么角色以及任何其他适当属性的详细信息。

  • Reci 1月前 0 只看Ta
    引用 15

    这是最完整的答案,考虑到了解释和可访问性。我认为这应该是可接受的答案,因为大多数其他答案仅处理点击,并且只是代码片段而没有任何解释。

  • 您实际上并不想绑定点击处理程序。您可以绑定点击处理程序,也可以处理用户没有鼠标的情况。它不会损害可访问性,它只会为使用鼠标的用户添加功能。向一组用户添加功能不会损害无法使用该功能的用户。您可以提供多种关闭对话框的方法这实际上是一个相当常见的逻辑谬误。即使其他用户没有受益,也完全可以向一组用户提供一项功能。我同意所有用户都应该能够获得良好的体验

  • @ICW,通过使用 blur 或 focusout 处理程序,您仍将完全支持鼠标和触摸用户,并且它还具有支持键盘用户的额外好处。我从来没有建议你不应该支持鼠标用户。

  • TICH 1月前 0 只看Ta
    引用 18

    看看这在 vanilla JS 中是什么样子会很有帮助。我知道这个问题是在 JQuery 中,但 14 年过去了,JQuery 答案占据了主导地位,以至于很难学习最新的解决方案。

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

    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 loads
    window.addEventListener("click", onClickOutside);
    

    ...最后是一些琐事

    // remove listener when destroying the widget
    window.removeEventListener("click", onClickOutside);
    
  • 这对我有用,除了我在 IF 语句中添加了 && !$(event.target).parents(\'#foo\').is(\'#foo\'),以便任何子元素在单击时都不会关闭菜单。

返回
作者最近主题: