无障碍菜单:反向Tab键导航功能实现求助
无障碍菜单反向Tab导航问题解决方案
核心问题分析
当前代码仅处理了正向Tab时的菜单展开/收起逻辑:聚焦顶级链接展开菜单,离开子菜单最后一个链接时收起。但反向Tab(Shift+Tab)时,焦点从子菜单第一个链接回到顶级链接,或者从顶级链接跳转到上一个菜单时,当前菜单无法正确收起,原因是缺少对焦点是否仍在当前菜单范围内的判断。
解决思路
- 为顶级链接添加
blur事件,判断失去焦点后,新的焦点元素是否属于当前菜单容器(包括子菜单),如果不属于则收起菜单 - 监听子菜单第一个链接的
keydown事件,处理Shift+Tab回到顶级链接时的菜单状态维持 - 新增收起其他已展开菜单的逻辑,确保同一时间仅一个菜单展开,符合无障碍导航预期
修改后的代码实现
const topLevelLinks = document.querySelectorAll('.menu__link') topLevelLinks.forEach(link => { const menuItem = link.parentElement; // 聚焦顶级链接时展开菜单,同时收起其他菜单 link.addEventListener('focus', function() { document.querySelectorAll('.menu__item.focus').forEach(item => { if (item !== menuItem) item.classList.remove('focus'); }); menuItem.classList.add('focus'); }); // 顶级链接失去焦点时,判断焦点是否仍在当前菜单内 link.addEventListener('blur', function() { setTimeout(() => { const isFocusInside = menuItem.contains(document.activeElement); if (!isFocusInside) { menuItem.classList.remove('focus'); } }, 0); }); if (link.nextElementSibling) { const subMenu = link.nextElementSibling; const subMenuLinks = subMenu.querySelectorAll('a'); const firstLink = subMenuLinks[0]; const lastLink = subMenuLinks[subMenuLinks.length - 1]; // 子菜单最后一个链接失去焦点时,判断焦点是否离开菜单 lastLink.addEventListener('blur', function() { setTimeout(() => { const isFocusInside = menuItem.contains(document.activeElement); if (!isFocusInside) { menuItem.classList.remove('focus'); } }, 0); }); // 处理子菜单第一个链接反向Tab回到顶级链接时的状态 firstLink.addEventListener('keydown', function(e) { if (e.shiftKey && e.key === 'Tab') { menuItem.classList.add('focus'); } }); } })
关键逻辑说明
setTimeout延迟判断:blur事件触发时新焦点元素尚未完全设置,延迟0毫秒可确保获取正确的document.activeElementmenuItem.contains(document.activeElement):判断当前焦点是否在菜单容器内(含顶级链接和子菜单),避免误收起菜单- 收起其他菜单:解决多菜单同时展开的混乱状态,提升导航体验
内容的提问来源于stack exchange,提问作者John Macpherson
相关产品推荐
相关产品推荐

