我有一些HTML菜单,当用户点击这些菜单的头部时,我会完全显示。当用户点击菜单区域外时,我想隐藏这些元素。
jQuery可以这样吗?
$("#menuscontainer").clickOutsideThisElement(function() {
// Hide the menus
});
我有一些HTML菜单,当用户点击这些菜单的头部时,我会完全显示。当用户点击菜单区域外时,我想隐藏这些元素。
jQuery可以这样吗?
$("#menuscontainer").clickOutsideThisElement(function() {
// Hide the menus
});
注意:使用
stopEventPropagation()
是应该避免的,因为它打破了DOM中的正常事件流。看到 本文 了解更多信息。考虑使用 这种方法 代替。
将单击事件附加到关闭窗口的文档正文。将单独的单击事件附加到容器,该容器将停止传播到文档正文。
$(window).click(function() {
//Hide the menus if visible
});
$('#menucontainer').click(function(event){
event.stopPropagation();
});
你可以听一个 点击 活动 document
然后确保 #menucontainer
通过使用不是祖先或被点击元素的目标 .closest()
。
如果不是,则单击的元素位于 #menucontainer
你可以安全地隐藏它。
$(document).click(function(event) {
if(!$(event.target).closest('#menucontainer').length) {
if($('#menucontainer').is(":visible")) {
$('#menucontainer').hide();
}
}
});
如果您打算关闭菜单并想要停止侦听事件,也可以在事件监听器之后进行清理。此函数将仅清除新创建的侦听器,并保留其他任何单击侦听器 document
。使用ES2015语法:
export function hideOnClickOutside(selector) {
const outsideClickListener = (event) => {
if (!$(event.target).closest(selector).length) {
if ($(selector).is(':visible')) {
$(selector).hide()
removeClickListener()
}
}
}
const removeClickListener = () => {
document.removeEventListener('click', outsideClickListener)
}
document.addEventListener('click', outsideClickListener)
}
对于那些不想使用jQuery的人。这是普通vanillaJS(ECMAScript6)中的上述代码。
function hideOnClickOutside(element) {
const outsideClickListener = event => {
if (!element.contains(event.target)) { // or use: event.target.closest(selector) === null
if (isVisible(element)) {
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可以在这里找到: https://developer.mozilla.org/en-US/docs/Web/API/Element/closest
如何检测元素外的点击?
这个问题如此受欢迎并且答案如此之多的原因在于它看起来很复杂。经过近八年的时间和几十个答案,我真的很惊讶地看到对可访问性的关注度很低。
当用户点击菜单区域外时,我想隐藏这些元素。
这是一个崇高的事业,而且是 实际 问题。问题的标题 - 这是大多数答案似乎试图解决的问题 - 包含一个不幸的红鲱鱼。
提示:这就是这个词 “点击”!
如果您绑定了点击处理程序以关闭对话框,那么您已经失败了。你失败的原因是不是每个人都会触发 click
事件。不使用鼠标的用户可以通过按下来转义对话框(并且您的弹出菜单可以说是一种对话框) 标签,然后他们将无法在不随后触发的情况下阅读对话框后面的内容 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>
如果您使用该演示超过一分钟,您应该很快就会看到问题。
第一个是对话框中的链接不可点击。尝试单击它或选项卡到它将导致对话关闭在交互发生之前。这是因为聚焦内部元素会触发a 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>
第二个问题是再次按下链接时对话框不会关闭。这是因为对话框失去焦点,触发关闭行为,之后链接单击触发对话框重新打开。
与前一个问题类似,需要管理焦点状态。鉴于状态更改已经排队,只需在对话框触发器上处理焦点事件:
这应该看起来很熟悉$('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>
如果您认为自己是通过处理焦点状态来完成的,那么您可以采取更多措施来简化用户体验。
这通常是一个“很好的”功能,但是当你有一个任何类型的模态或弹出窗口时,这是常见的 退出 钥匙将关闭它。
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角色和属性, 但是,我 高度 建议实施者参考规范,了解他们应该使用哪些角色以及任何其他适当的属性。
这里的其他解决方案对我不起作用所以我不得不使用:
if(!$(event.target).is('#foo'))
{
// hide menu
}
我有一个类似于Eran示例的应用程序,除了我在打开菜单时将click事件附加到正文...有点像这样:
$('#menucontainer').click(function(event) {
$('html').one('click',function() {
// Hide the menus
});
event.stopPropagation();
});
有关的更多信息 jQuery的 one()
功能
$("#menuscontainer").click(function() {
$(this).focus();
});
$("#menuscontainer").blur(function(){
$(this).hide();
});
对我来说就好了。
当a时发生以下情况 clickoutside handler(WLOG)绑定到一个元素:
因此没有任何事件被阻止传播和其他事件 点击 处理程序可以在具有外部处理程序的元素“之上”使用。
这完全适合我!
$('html').click(function (e) {
if (e.target.id == 'YOUR-DIV-ID') {
//do something
} else {
//do something
}
});