代码之家  ›  专栏  ›  技术社区  ›  daGUY

为什么$(“a:focus”).length的值为0,除非包装在setTimeout()中?

  •  0
  • daGUY  · 技术社区  · 6 年前

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

    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();
            }
        });
    });
    

    现在, $nav.find("a:focus").length 评估到 1 每次我打 标签 ,直到i选项卡位于菜单外部,此时它的计算结果为 把它关上。

    这正是我想要的工作方式,但为什么 设置TimeTo() 必要吗?

    1 回复  |  直到 6 年前
        1
  •  2
  •   Tyler Roper    6 年前

    为什么不起作用?

    正如埃帕斯卡雷罗在评论中指出的那样, blur 在下一个元素有焦点之前发生。

    为什么 setTimeout() ,即使使用 0ms 耽搁,工作?

    相信 这与渲染引擎有关。当你做一个 设置TimeTo() ,函数将被“排队” 后面 渲染引擎的ui更新(它关注下一个元素)。尽管在 时间 , the 设置TimeTo() 确保进行检查 之后 用户界面更新。

    事件顺序:

    1. Fire blur event
    2. Update UI
    

    事件顺序 设置TimeTo() :

    1. Fire blur event (queue new function) ──┐
    2. Update UI                              |
    3. Run the queued function    <───────────┘
    

    有别的选择吗?

    这个 模糊 事件的 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>