javascript - 为什么 $ ("a:focus").length 的计算结果为 0,除非包含在 setTimeout() 中?

标签 javascript jquery settimeout onblur onfocus

我正在将键盘导航添加到我网站的主菜单中,并且我希望当用户在主菜单之外进行选项卡时关闭任何打开的子菜单。所以我有以下功能:

var $nav = $(".navigation nav.main"),
    $navItems = $nav.find("a");

$navItems.on("blur", function() {
    if ($nav.find("a:focus").length === 0) {
        closeMenu();
    }
});

我期望它的工作方式是这样的:

  1. 每当菜单中的任何链接失去焦点时...
  2. 如果菜单中没有具有焦点的链接,请关闭菜单

但实际发生的是 $(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 a 0ms 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/

相关文章:

javascript - 这个 getJson 调用有什么问题

javascript - 如果我使用 Javascript 将 <option> 设置为选中,是否会触发更改事件?

jquery - 什么选择器效率更高

javascript - 如何在按钮内进行倒计时?

带有 setTimeout 的 jQuery live 函数

javascript - 如何在 three.js 或 WebGL 中模拟 "overflow: hidden"?

javascript - 编写异步 Vows.js 测试时出错

javascript - 将Class添加到具有特定属性的div

javascript - 从 Javascript 对象创建 HTML 标签

javascript - setInterval() 序列中的 setTimeout() 仅触发一次