为何使用Tab和箭头键无法聚焦带aria-disabled的菜单项?
问题代码
<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"的菜单项会被完全跳过,无法聚焦。移除该属性后,该菜单项能正常被键盘访问,其他菜单项无此问题。
原因分析
- 你的菜单项设置了
tabIndex="-1",这类元素本身就不能通过Tab键直接聚焦,只能通过编程式聚焦(比如箭头键导航触发)——这是菜单组件的标准交互模式:用户先通过Tab聚焦到菜单容器,再用箭头键在内部导航菜单项。 - 浏览器或屏幕阅读器的原生菜单导航逻辑会自动跳过带
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
相关产品推荐
相关产品推荐

