我知道这是一个常见问题,但是我所见的答案都无法解决我的问题,如果我错过了一个,可以道歉,很明显可以将其删除/标记为重复...
标记
<div class="has-dropdown">
<button class="js-dropdown-trigger">
Dropdown
</button>
<div class="dropdown">
<div class="dropdown__item">
Some random text with a <a href="#" class="stop-propagation">link</a> in it.
</div>
<div class="dropdown__divider"></div>
<div class="dropdown__item">
<a href="#">Item One</a>
</div>
<div class="dropdown__item">
<a href="#">Item Two</a>
</div>
<div class="dropdown__item">
<a href="#">Item Three</a>
</div>
</div>
</div>
脚本
$('.has-dropdown').off().on('click', '.js-dropdown-trigger', (event) => {
const $dropdown = $(event.currentTarget).next('.dropdown');
if (!$dropdown.hasClass('is-active')) {
$dropdown.addClass('is-active');
} else {
$dropdown.removeClass('is-active');
}
});
$('.has-dropdown').on('focusout', (event) => {
const $dropdown = $(event.currentTarget).children('.dropdown');
$dropdown.removeClass('is-active');
});
样式
.has-dropdown {
display: inline-flex;
position: relative;
}
.dropdown {
background-color: #eee;
border: 1px solid #999;
display: none;
flex-direction: column;
position: absolute;
top: 100%;
left: 0;
width: 300px;
margin-top: 5px;
}
.dropdown.is-active {
display: flex;
}
.dropdown__item {
padding: 10px;
}
.dropdown__divider {
border-bottom: 1px solid #999;
}
小提琴
http://jsfiddle.net/joemottershaw/3yzadmek/
这非常简单,单击js-dropdown-trigger
可以使is-active
下拉菜单很好地切换,在has-dropdown
容器外部单击也可以删除is-active
下拉菜单。
除了,我期望发生的事情集中在has-dropdown
元素的后代元素(单击或制表符)上,这意味着不应focusout
事件处理程序被触发,因为您仍然专注于has-dropdown
容器的后代元素。
focusout
事件在或任何元素时发送到元素 内部,失去了焦点。这与中的模糊事件不同 它支持检测对后代元素的焦点丢失
我知道我可以删除focusout
事件处理程序,并使用类似以下内容的方法:
$(document).on('click', (event) =>{
const $dropdownContainer = $('.has-dropdown');
if (!$dropdownContainer.is(event.target) && $dropdownContainer.has(event.target).length === 0) {
$dropdownContainer.find('.dropdown').removeClass('is-active');
}
});
这有效,但是如果您单击触发器,然后通过链接进行制表,则当您通过最后一个链接进行制表时,下拉列表仍然可见。只是努力寻找最佳的解决方案来保持事物的可访问性。
如果可能的话,我想坚持使用focusout
方法。
根据darshanags答案进行了更新
尽管更新的脚本适用于单个元素,但将其他元素添加到body
会导致focusout
不再按预期工作。我认为这是因为if
语句似乎是正确的,即使焦点集中在has-dropdown
容器之后的任何元素上,而不仅仅是后代吗?如果您要更新HTML并在下拉菜单之后添加更多可聚焦元素(例如输入),则为原因。当从has-dropdown
容器中的最后一个可聚焦元素到输入之间切换时,下拉菜单保持活动状态。仅当下拉列表是DOM中的最后一个元素时,它才起作用,并且仅在完全失去对DOM的焦点时才触发。
答案 0 :(得分:1)
您的代码已经差不多了-但我认为需要更加清楚focusout
与本机不可聚焦元素(即div,p)及其后代的工作方式,成为可聚焦的元素(输入,锚点)。
当容器绑定到包含可聚焦元素的focusout
事件时,{strong}每次都会触发focusout
事件它的任何可聚焦子元素都失去焦点-这可能通过键盘导航或单击另一个子元素或容器本身。我设置了一个小提琴来演示这一点:https://jsfiddle.net/darshanags/v5gk2cz8/-每次容器获得焦点或失去焦点时,都会发出控制台消息。
问:那么,为什么菜单隐藏在问题中给出的示例中?
A:当您单击按钮时,该菜单将变为可见,单击该按钮可使按钮将焦点对准自身。但是,一旦您单击菜单或菜单元素内的锚点,该按钮就会失去焦点-这又触发了父元素的'focusout'事件并导致菜单隐藏。发生这种情况是因为focusout
事件支持事件冒泡。
问:我们如何解决这个问题?
A1.1:我们通过给它一个tabindex来使父元素可聚焦:
<div class="has-dropdown" tabindex="0">
这解决了一些重要的事情:
div
元素提供焦点轮廓-依次增强元素的可访问性。div
元素上注册与焦点相关的事件-在本例中为focusout
。如果省略了tabindex
,我们将需要添加其他JavaScript来弥补丢失的功能。 A1.2:在focusout
事件处理程序中,我们检查获得焦点的元素是否是父元素的后代,如果是,则不删除{{1} }菜单中的课程。
完整的示例代码:
is-active
$('.has-dropdown').off().on('click', '.js-dropdown-trigger', (event) => {
const $dropdown = $(event.currentTarget).next('.dropdown');
if (!$dropdown.hasClass('is-active')) {
$dropdown.addClass('is-active');
} else {
$dropdown.removeClass('is-active');
}
});
$('.has-dropdown').on('focusout', function(event) {
const $reltarget = $(event.relatedTarget);
const $currenttarget = $(event.currentTarget);
const $dropdown = $currenttarget.children('.dropdown');
// remove 'is-active' class only if the element
// that is gaining focus is not a child of the parent.
// parent = div.has-dropdown
if (!$reltarget.closest('.has-dropdown').is($currenttarget)) {
$dropdown.removeClass('is-active');
}
});
.has-dropdown {
display: inline-flex;
position: relative;
}
.dropdown {
background-color: #eee;
border: 1px solid #999;
display: none;
flex-direction: column;
position: absolute;
top: 100%;
left: 0;
width: 300px;
margin-top: 5px;
}
.dropdown.is-active {
display: flex;
}
.dropdown__item {
padding: 10px;
}
.dropdown__divider {
border-bottom: 1px solid #999;
}
我已经分叉了原始的小提琴,并对其进行了修改以显示其实际效果。您可以在这里找到经过修改的小提琴:http://jsfiddle.net/darshanags/60jnusvk/。
其他有用信息:
我使用<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="has-dropdown" tabindex="0">
<button class="js-dropdown-trigger">
Dropdown
</button>
<div class="dropdown">
<div class="dropdown__item">
Some random text with a <a href="#" class="stop-propagation">link</a> in it.
</div>
<div class="dropdown__divider"></div>
<div class="dropdown__item">
<a href="#">Item One</a>
</div>
<div class="dropdown__item">
<a href="#">Item Two</a>
</div>
<div class="dropdown__item">
<a href="#">Item Three</a>
</div>
</div>
</div>
<div class="has-dropdown" tabindex="0">
<button class="js-dropdown-trigger">
Dropdown
</button>
<div class="dropdown">
<div class="dropdown__item">
Some random text with a <a href="#" class="stop-propagation">link</a> in it.
</div>
<div class="dropdown__divider"></div>
<div class="dropdown__item">
<a href="#">Item One</a>
</div>
<div class="dropdown__item">
<a href="#">Item Two</a>
</div>
<div class="dropdown__item">
<a href="#">Item Three</a>
</div>
</div>
</div>
<input name="tf" type="text"/>
来确定每次触发event.relatedTarget
事件时都会获得焦点的元素。有关focusout
的更多信息,请参见:https://api.jquery.com/event.relatedTarget/。
更新
我已经重构了一些原始代码,现在更加简单了:http://jsfiddle.net/darshanags/60jnusvk/24/。我将把这个小提琴和原始的东西留作参考。