当一个元素或其中任何一个后代失去焦点时,删除一个类

时间:2016-06-22 17:50:23

标签: javascript jquery

我试图让我的导航系统更易于访问,而且我很难搞清楚其中的一部分。这是我喜欢的功能列表

  • 通过键盘导航。这很好。
  • 点击/按下子菜单前的按钮,切换它的活动状态。这很好。
  • 当子菜单或其任何后代失去焦点时关闭子菜单。这就是我遇到麻烦的地方。

问题在于,我似乎无法准确地判断出这个元素或它的孩子是否有焦点。第一次按" tab"它可以正常工作在键盘上,"但在突出显示子列表中的第二个元素后,菜单将关闭。

这里是我的所有JS以及一些CSS和HTML示例。

JS:

// toggle the is-active class and aria values on button click
jQuery(".menu-list_toggle").bind("click", function(e) {
    e.preventDefault();

    if (jQuery(this).next(".menu-list[aria-hidden]").attr("aria-hidden") === "true") {
        // mark all sibling menus as inactive
        jQuery(this).closest(".menu-list_item").siblings().removeClass("is-active");

        // mark menu list as active
        jQuery(this).closest(".menu-list_item").addClass("is-active");
        jQuery(this).next(".menu-list[aria-hidden]").attr("aria-hidden", "false");
    } else {
        // mark menu list as inactive
        jQuery(this).closest(".menu-list_item").removeClass("is-active");
        jQuery(this).next(".menu-list[aria-hidden]").attr("aria-hidden", "true");
    }
});

// remove is-active and aria values on blur - HAVING TROUBLE HERE!
jQuery(".menu-list").bind("focusout", function() {
    if (jQuery(this).has(document.activeElement).length === 0) {
        jQuery(this).closest(".menu-list_item").removeClass("is-active");
        jQuery(this).attr("aria-hidden", "true");
    }
});

CSS:

[aria-hidden=true] {
  display: none;
}

.is-active {
  background: teal;
}

HTML:

<ul class="menu-list">
    <li class="menu-list_item">
        <a class="menu-list_link" href="#">Some Link</a>
        <button class="menu-list_toggle">Toggle Menu</button>
        <ul class="menu-list" aria-hidden="true">
            <li class="menu-list_item">
                <a class="menu-list_link" href="#">Some Link</a>
            </li>
            <li class="menu-list_item">
                <a class="menu-list_link" href="#">Some Link</a>
                <button class="menu-list_toggle">Toggle Menu</button>
                <ul class="menu-list" aria-hidden="true">
                    <li class="menu-list_item">
                        <a class="menu-list_link" href="#">Some Link</a>
                    </li>
                    <li class="menu-list_item">
                        <a class="menu-list_link" href="#">Some Link</a>
                    </li>
                    <li class="menu-list_item">
                        <a class="menu-list_link" href="#">Some Link</a>
                    </li>
                </ul>
            </li>
            <li class="menu-list_item">
                <a class="menu-list_link" href="#">Some Link</a>
            </li>
            <li class="menu-list_item">
                <a class="menu-list_link" href="#">Some Link</a>
            </li>
        </ul>
    </li>
    <li class="menu-list_item">
        <a class="menu-list_link" href="#">Some Link</a>
    </li>
    <li class="menu-list_item">
        <a class="menu-list_link" href="#">Some Link</a>
    </li>
</ul>

JS Fiddle

理想情况下,我希望用vanilla JavaScript而不是jQuery来完成所有这些操作,但我想我可以在核心概念运行后再回过头来进行重构。

1 个答案:

答案 0 :(得分:1)

这很简单,你可以这样做:

此外,将事件监听器附加到.menu-list并将blur事件从.menu-list_link委托给它,这样您就不会有很多事件监听器。

jQuery(".menu-list").on("blur", ".menu-list_link", function(e) {
  var tThis = jQuery(this);
  var tThisParent = tThis.closest('.menu-list_item');
  var tThisGrandParent = tThisParent.closest('.menu-list');
  var tThisGreatGrandParent = tThisGrandParent.closest('.menu-list_item');
  if (tThisParent.is(':last-child')) {
    tThisGrandParent.attr("aria-hidden", "true");
    tThisGreatGrandParent.removeClass("is-active");
  }
});

仅当丢失focus的项目是其相应子菜单中的最后一项时,它才会关闭子菜单菜单。

演示: https://jsfiddle.net/r0kz0g69/2/

此外,如果您将整个菜单放在容器中,您可以将事件监听器附加到它而不是.menu-list s,例如一个.menu-container,所以你在DOM元素上只有一个事件监听器。

jQuery(".menu-container").on("blur", ".menu-list_link", function() {
  var tThis = jQuery(this);
  var tThisParent = tThis.closest('.menu-list_item');
  var tThisGrandParent = tThisParent.closest('.menu-list');
  var tThisGreatGrandParent = tThisGrandParent.closest('.menu-list_item');
  if (tThisParent.is(':last-child')) {
    tThisGrandParent.attr("aria-hidden", "true");
    tThisGreatGrandParent.removeClass("is-active");
  }
});

演示2: https://jsfiddle.net/r0kz0g69/3/

如果失去焦点的项目是最外面的子菜单,那么不要隐藏菜单列表,这样一点点改进,所以最外面的菜单保持可见。

jQuery(".menu-container").on("blur", ".menu-list_link", function() {
  var tThis = jQuery(this);
  var tThisParent = tThis.closest('.menu-list_item');
  var tThisGrandParent = tThisParent.closest('.menu-list');
  var tThisGreatGrandParent = tThisGrandParent.closest('.menu-list_item');
  if (tThisParent.is(':last-child')) {
    if(!tThisGrandParent.is('.menu-container > .menu-list')) {
        tThisGrandParent.attr("aria-hidden", "true");
    }
    tThisGreatGrandParent.removeClass("is-active");
  }
});

演示3: https://jsfiddle.net/r0kz0g69/4/