如何避免Tab键聚焦折叠汉堡菜单并锁定菜单焦点范围
无障碍汉堡菜单实现方案
针对你提出的两个核心无障碍需求,以下是基于HTML、CSS和JS的落地实现方案:
一、基础HTML结构
用语义化标签搭配ARIA属性,确保屏幕阅读器能正确识别菜单状态:
<!-- 汉堡菜单触发按钮 --> <button id="menu-toggle" aria-expanded="false" aria-label="打开主菜单"> ☰ </button> <!-- 菜单容器 --> <nav id="main-menu" aria-hidden="true" aria-labelledby="menu-toggle"> <!-- 菜单关闭按钮 --> <button id="menu-close" aria-label="关闭主菜单">×</button> <ul> <li><a href="/home">首页</a></li> <li><a href="/about">关于我们</a></li> <li><a href="/services">服务</a></li> <li><a href="/contact">联系我们</a></li> </ul> </nav>
二、CSS样式
控制菜单的显示/隐藏状态,同时确保折叠时菜单完全脱离视觉流:
#main-menu { position: fixed; top: 0; left: 0; width: 100%; height: 100vh; background: #fff; padding: 2rem; transform: translateX(-100%); transition: transform 0.3s ease; z-index: 1000; } #main-menu.open { transform: translateX(0); } /* 折叠状态下隐藏关闭按钮 */ #menu-close { display: none; margin-bottom: 2rem; } #main-menu.open #menu-close { display: block; }
三、JavaScript核心功能实现
1. 菜单切换与基础无障碍状态管理
const menuToggle = document.getElementById('menu-toggle'); const mainMenu = document.getElementById('main-menu'); const menuClose = document.getElementById('menu-close'); const menuLinks = mainMenu.querySelectorAll('a'); // 切换菜单状态的核心函数 function toggleMenu(isOpen) { mainMenu.classList.toggle('open', isOpen); menuToggle.setAttribute('aria-expanded', isOpen); mainMenu.setAttribute('aria-hidden', !isOpen); // 控制菜单项的焦点可访问性:折叠时设为-1(跳过),打开时设为0(可聚焦) menuLinks.forEach(link => { link.setAttribute('tabindex', isOpen ? '0' : '-1'); }); // 菜单打开时自动聚焦到关闭按钮,符合无障碍导航逻辑 if (isOpen) { menuClose.focus(); } } // 绑定按钮点击事件 menuToggle.addEventListener('click', () => toggleMenu(true)); menuClose.addEventListener('click', () => toggleMenu(false)); // 菜单项点击后自动关闭菜单 menuLinks.forEach(link => { link.addEventListener('click', () => toggleMenu(false)); });
2. 焦点陷阱实现(限制Tab在菜单内部循环)
function handleFocusTrap(e) { // 菜单未打开时不处理 if (!mainMenu.classList.contains('open')) return; const focusableElements = mainMenu.querySelectorAll('button, a'); const firstElement = focusableElements[0]; const lastElement = focusableElements[focusableElements.length - 1]; // Tab键正向循环:最后一个元素按Tab时跳回第一个 if (e.key === 'Tab' && !e.shiftKey) { if (document.activeElement === lastElement) { e.preventDefault(); firstElement.focus(); } } // Shift+Tab反向循环:第一个元素按Shift+Tab时跳转到最后一个 else if (e.key === 'Tab' && e.shiftKey) { if (document.activeElement === firstElement) { e.preventDefault(); lastElement.focus(); } } } // 监听全局键盘事件 document.addEventListener('keydown', handleFocusTrap);
3. Esc键关闭菜单
document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && mainMenu.classList.contains('open')) { toggleMenu(false); // 关闭后聚焦回汉堡按钮,保持导航连贯性 menuToggle.focus(); } });
关键逻辑说明
- 折叠状态禁止焦点访问:通过
tabindex="-1"让Tab键跳过菜单项,同时aria-hidden="true"告诉屏幕阅读器忽略该区域内容。 - 焦点陷阱:监听Tab/Shift+Tab事件,在焦点到达菜单边界时强制跳转,确保焦点始终在菜单内部。
- 无障碍属性同步:菜单切换时同步更新
aria-expanded和aria-hidden,保证屏幕阅读器能实时感知状态变化。
内容的提问来源于stack exchange,提问作者Dakingda
相关产品推荐
相关产品推荐

