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

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

HamZa Samha 1月前

79 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)
  • 如何检测元素外部的点击?

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

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

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

    提示:它是单词“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 角色和属性 ,但我 强烈 建议实施者参考规范以了解他们应该使用什么角色以及任何其他适当属性的详细信息。

返回
作者最近主题: