我正在将键盘导航添加到我网站的主菜单中,并且我希望当用户在主菜单之外进行选项卡时关闭任何打开的子菜单。所以我有以下功能:
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
,即使当我可以直接在屏幕上看到菜单中确实有一个带有焦点的链接时。
但是,如果我将 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()
?
最佳答案
Why doesn't it work?
正如 epascarello 在评论中指出的那样,您的模糊
是在下一个元素获得焦点之前发生的。
Why does
setTimeout()
, even with a0ms
delay, work?
我相信这与渲染引擎有关。当您执行 setTimeout()
时,该函数会在渲染引擎的 UI 更新(聚焦下一个元素)之后“排队”。尽管时间没有差异,setTimeout()
确保在 UI 更新之后进行检查。
事件顺序:
1. Fire blur event
2. Update UI
使用 setTimeout()
的事件顺序:
1. Fire blur event (queue new function) ──┐
2. Update UI |
3. Run the queued function <───────────┘
Is there an alternative?
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>
关于javascript - 为什么 $ ("a:focus").length 的计算结果为 0,除非包含在 setTimeout() 中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53673139/