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

如何避免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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 18:10:35