使用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
相关产品推荐
相关产品推荐

