我正在将键盘导航添加到站点的主菜单中,并且当用户在主菜单之外单击时,我希望任何打开的子菜单都关闭。所以我有以下功能:
var $nav = $(".navigation nav.main"),
$navItems = $nav.find("a");
$navItems.on("blur", function() {
if ($nav.find("a:focus").length === 0) {
closeMenu();
}
});
我希望它起作用的方式是这样的:
但是实际上发生的是$(nav.find("a:focus").length)
始终的计算结果为0
,即使我直接在屏幕上看到菜单中确实存在一个具有焦点的链接。 / p>
但是,如果我将setTimeout()
包装在条件中,那么它将按我期望的那样工作:
var $nav = $(".navigation nav.main"),
$navItems = $nav.find("a");
$navItems.on("blur", function() {
setTimeout(function() {
if ($nav.find("a:focus").length === 0) {
closeMenu();
}
});
});
现在,每次我按下 Tab 时,$nav.find("a:focus").length
都将评估为1
,直到我在菜单外进行标签切换,此时它的值为0
,然后关闭它。
这正是我希望它起作用的方式,但是为什么setTimeout()
是必需的?
答案 0 :(得分:2)
为什么不起作用?
正如epascarello在评论中指出的那样,您的blur
发生在下一个元素被关注之前。
为什么
setTimeout()
即使有0ms
的延迟也能工作?
我相信这与渲染引擎有关。当您执行setTimeout()
时,该函数将在呈现引擎的UI更新(集中在下一个元素上)的后面后排队。尽管时间 没有什么区别,但是setTimeout()
确保在用户界面更新后 进行检查。
事件顺序:
1. Fire blur event
2. Update UI
带有setTimeout()
的事件的顺序:
1. Fire blur event (queue new function) ──┐
2. Update UI |
3. Run the queued function <───────────┘
还有其他选择吗?
blur
事件的relatedTarget
指示哪个元素将成为焦点。
通过知道将要获得焦点的元素,您可以使用jQuery的.filter()
来确定它是否是导航项目。
var $nav = $(".navigation nav.main"),
$navItems = $nav.find("a");
$navItems.on("blur", function(e) {
var $focusedElement = $(e.relatedTarget);
var isNavItem = !!$navItems.filter($focusedElement).length;
if (!isNavItem)
console.log("The focused element is not a nav item.");
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="navigation">
<nav class="main">
<a href="#">Link 1</a>
<a href="#">Link 2</a>
</nav>
</div>
<a href="#">Non-nav link</a>