You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用shift+tab触发focusout时,子菜单样式未正常隐藏

Shift+Tab聚焦菜单后失焦,子菜单无法隐藏的问题修复

问题背景

需求是通过Tab键聚焦<li>内的按钮时显示关联子菜单,失去焦点时自动隐藏。当前实现中,正常按Tab键移出子菜单时功能正常,但使用Shift+Tab反向聚焦到目标<li>的按钮后,再失焦时,focusout事件中的隐藏样式(visibility: hidden、opacity: 0)不生效,仅修改背景色的代码能正常执行。

完整代码片段

JavaScript

const navButtons = document.querySelectorAll('[data-header-button]');

navButtons.forEach((element) => {
  const closestListElement = element.closest('li');
  const [submenu] =
  closestListElement.getElementsByClassName('dropdown-nav__list');

  if (!submenu) {
    return;
  }

  const listener = (e) => {
    submenu.style.visibility = 'visible';
    submenu.style.opacity = 1;
  };

  element.addEventListener('focus', listener);

  submenu.addEventListener('focusout', () => {
    if (submenu.matches(':focus-within:not(:focus)') === false) {
      submenu.style.visibility = 'hidden';
      submenu.style.backgroundColor = 'red';
      submenu.style.opacity = 0;
    }
  });
});

CSS

.c-header__navigation-desktop ul {
  display: none;
}

.c-header__navigation-desktop:focus-within ul {
  display: list-item;
}

HTML

<ul class="c-header__navigation c-header__navigation-desktop" data-header-link-widget="">
  <li data-header-li="" class="c-header__navigation-item dropdown-nav">
    <a data-header-button="" class="c-header__link  " href="https://demo332.jibeapply.com/internal/jobs">Search Jobs</a>
    <ul class="dropdown-nav__list">
      <li class="dropdown-nav__item">
        <a data-header-button="" class="nested-link  " href="k">some link 1</a>
      </li>
      <li class="dropdown-nav__item">
        <a data-header-button="" class="nested-link  " href="o">some link 2</a>
      </li>
    </ul>
  </li>
  <li data-header-li="" class="c-header__navigation-item ">
    <a data-header-button="" class="c-header__link  " href="">Departments</a>
  </li>
  <li data-header-li="" class="c-header__navigation-item ">
    <a data-header-button="" class="c-header__link  " href="https://demo332.jibeapply.com/internal/jobs?view=personalization">Benefits</a>
  </li>
  <li data-header-li="" class="c-header__navigation-item ">
    <a data-header-button="" class="c-header__link  " href="">Internships</a>
  </li>
  <li data-header-li="" class="c-header__navigation-item ">
    <a data-header-button="" class="c-header__link  " href="">Diversity and Inclusion</a>
  </li>
</ul>

问题原因

原判断条件submenu.matches(':focus-within:not(:focus)') === false仅针对子菜单自身的聚焦状态做判断,忽略了焦点在父<li>容器内的情况。当用Shift+Tab反向聚焦到父按钮时,焦点仍处于父<li>范围内,此时子菜单不应隐藏,但原逻辑的判断条件会误判,导致隐藏代码不执行。

修复方案

方案一:基于父容器的:focus-within状态判断

修改focusout事件中的判断逻辑,检查整个父<li>是否还处于聚焦范围内:

submenu.addEventListener('focusout', () => {
  // 当焦点完全离开父li容器时,隐藏子菜单
  if (!closestListElement.matches(':focus-within')) {
    submenu.style.visibility = 'hidden';
    submenu.style.backgroundColor = 'red';
    submenu.style.opacity = 0;
  }
});

方案二:利用focusout的relatedTarget判断焦点去向

通过事件对象的relatedTarget属性,直接检查焦点是否还在父<li>的范围内:

submenu.addEventListener('focusout', (e) => {
  // 若焦点移出了父li的范围,则隐藏子菜单
  if (!closestListElement.contains(e.relatedTarget)) {
    submenu.style.visibility = 'hidden';
    submenu.style.backgroundColor = 'red';
    submenu.style.opacity = 0;
  }
});

可选优化:统一CSS与JS的显示逻辑

原CSS与JS的显示控制存在冲突,建议统一用:focus-within实现样式切换,减少JS依赖:

/* 默认隐藏子菜单 */
.dropdown-nav__list {
  visibility: hidden;
  opacity: 0;
  transition: visibility 0s, opacity 0.2s ease; /* 添加过渡动画提升体验 */
}

/* 父li聚焦或子菜单内元素聚焦时显示 */
.dropdown-nav:focus-within .dropdown-nav__list,
.dropdown-nav__list:focus-within {
  visibility: visible;
  opacity: 1;
}

这种纯CSS方案可覆盖大部分场景,若需兼容旧浏览器,再保留JS逻辑即可。

效果验证

修改后,无论是正向Tab移出子菜单,还是反向Shift+Tab聚焦父按钮后再移出,子菜单都会在焦点完全离开整个菜单容器时正确隐藏,同时背景色修改也能正常执行。

内容的提问来源于stack exchange,提问作者ABGR

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 17:45:00