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

为何使用Tab和箭头键无法聚焦带aria-disabled的菜单项?

带aria-disabled的menuitem无法通过键盘聚焦的问题

问题代码

<li class="item1" role="menuitem" tabindex="-1">
  <svg>...</svg>
  <span>Item #1</span>
</li>

<li class="item2" role="menuitem" tabindex="-1">
  <svg>...</svg>
  <span>Item #2</span>
</li>

<li class="item3" role="menuitem" tabindex="-1">
  <svg>...</svg>
  <span>Item #3</span>
</li>

<li class="item4" role="menuitem" tabindex="-1" aria-disabled="true">
  <svg>...</svg>
  <span>Item #4</span>
</li>

问题描述

按aria-disabled的规范,设置该属性的元素仍应允许通过箭头键访问(可聚焦),但实际操作中,第四个带aria-disabled="true"的菜单项会被完全跳过,无法聚焦。移除该属性后,该菜单项能正常被键盘访问,其他菜单项无此问题。

原因分析

  1. 你的菜单项设置了tabIndex="-1",这类元素本身就不能通过Tab键直接聚焦,只能通过编程式聚焦(比如箭头键导航触发)——这是菜单组件的标准交互模式:用户先通过Tab聚焦到菜单容器,再用箭头键在内部导航菜单项。
  2. 浏览器或屏幕阅读器的原生菜单导航逻辑会自动跳过带aria-disabled的menuitem,虽然规范提到aria-disabled元素仍可聚焦,但针对菜单这类复合组件,原生逻辑默认会忽略禁用项。

解决方案

要让禁用的menuitem能被键盘聚焦,必须手动实现菜单的键盘导航逻辑,具体步骤如下:

  • 保留aria-disabled="true",用于告知辅助技术该元素不可用,同时添加视觉样式标记禁用状态。
  • 手动监听菜单的键盘事件(箭头键、回车/空格),控制菜单项的聚焦逻辑,确保箭头键能定位到禁用项。
  • 聚焦到禁用项后,阻止触发任何交互事件(点击、回车/空格操作)。

修改后的示例代码

<!-- 给菜单添加容器并设置role="menu",符合WAI-ARIA规范 -->
<ul role="menu" id="navMenu">
  <li class="item1" role="menuitem" tabindex="-1">
    <svg>...</svg>
    <span>Item #1</span>
  </li>
  <li class="item2" role="menuitem" tabindex="-1">
    <svg>...</svg>
    <span>Item #2</span>
  </li>
  <li class="item3" role="menuitem" tabindex="-1">
    <svg>...</svg>
    <span>Item #3</span>
  </li>
  <li class="item4" role="menuitem" tabindex="-1" aria-disabled="true">
    <svg>...</svg>
    <span>Item #4</span>
  </li>
</ul>

<script>
const menuContainer = document.getElementById('navMenu');
const menuItems = Array.from(menuContainer.querySelectorAll('[role="menuitem"]'));
let activeIndex = -1;

// 菜单容器获得焦点时,默认聚焦第一个菜单项
menuContainer.addEventListener('focus', () => {
  if (activeIndex === -1) {
    activeIndex = 0;
    menuItems[activeIndex].focus();
  }
});

// 处理箭头键导航逻辑
menuContainer.addEventListener('keydown', (e) => {
  switch(e.key) {
    case 'ArrowDown':
      e.preventDefault();
      // 循环切换到下一个菜单项(包含禁用项)
      activeIndex = (activeIndex + 1) % menuItems.length;
      menuItems[activeIndex].focus();
      break;
    case 'ArrowUp':
      e.preventDefault();
      activeIndex = activeIndex === 0 ? menuItems.length - 1 : activeIndex - 1;
      menuItems[activeIndex].focus();
      break;
    case 'Enter':
    case ' ':
      e.preventDefault();
      // 禁用项不触发任何操作
      if (menuItems[activeIndex].hasAttribute('aria-disabled')) return;
      // 此处添加正常菜单项的交互逻辑
      console.log(`触发${menuItems[activeIndex].querySelector('span').textContent}`);
      break;
  }
});

// 禁用项点击时阻止默认行为
menuItems.forEach(item => {
  if (item.hasAttribute('aria-disabled')) {
    item.addEventListener('click', (e) => e.preventDefault());
  }
});
</script>

<style>
/* 添加禁用项的视觉样式,明确告知用户状态 */
[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
}
</style>

关键说明

菜单组件的核心是容器控制内部导航,不能依赖原生Tab键直接聚焦菜单项,这是符合WAI-ARIA菜单规范的设计。手动实现导航逻辑后,既能保证禁用项可被聚焦(让用户明确知道该选项存在但不可用),又能避免误操作,同时兼容辅助技术。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 05:18:16