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

如何实现可访问导航的子菜单点击切换(无需硬编码ID)

解决方案:无需ID的通用子菜单切换 + 无障碍优化

不用硬编码ID,更不用动态生成data-id,直接靠DOM层级关系就能精准定位当前按钮对应的子菜单,同时给你补上关键的无障碍优化点:

一、核心实现:通用点击处理函数

替换你原有的JS,用DOM遍历替代全局选择,同时补上键盘支持:

// 页面加载完成后绑定事件,别用内联onclick,维护起来更方便
document.addEventListener('DOMContentLoaded', function() {
  const toggleButtons = document.querySelectorAll('.menu-item button');
  
  toggleButtons.forEach(button => {
    button.addEventListener('click', handleSubmenuToggle);
    // 支持键盘回车/空格触发,符合无障碍规范
    button.addEventListener('keydown', function(e) {
      if (e.key === 'Enter' || e.key === ' ') {
        e.preventDefault();
        handleSubmenuToggle.call(this);
      }
    });
  });
});

function handleSubmenuToggle() {
  // 找到当前按钮所在的父菜单容器
  const parentMenuItem = this.closest('.menu-item');
  // 定位该容器下的子菜单
  const submenu = parentMenuItem.querySelector('.sub-menu-items');
  
  // 切换显示状态
  submenu.classList.toggle('show');
  
  // 更新无障碍属性,告诉屏幕阅读器菜单状态
  const isExpanded = this.getAttribute('aria-expanded') === 'true';
  this.setAttribute('aria-expanded', !isExpanded);
}

二、HTML无障碍修正

你的原HTML有几个语义化问题,调整后更符合无障碍标准:

<header>
  <nav aria-label="Empower Reservation Navigation" class="nav-menu-container">
    <ul class="menu-items">
      <li class="menu-item slds-is-relative">
        <!-- 用aria-expanded替代aria-pressed,语义更准确;aria-haspopup告诉屏幕阅读器这是弹出菜单 -->
        <button aria-expanded="false" aria-haspopup="true">Menu Item 1 <i class="fa-solid fa-chevron-down"></i></button>
        <!-- 子菜单隐藏时设aria-hidden="true",屏幕阅读器会忽略 -->
        <ul class="sub-menu-items" aria-hidden="true">
          <li><a href="#">Sub Menu Item</a></li>
          <li><a href="#">Sub Menu Item 2</a></li>
          <li><a href="#">Sub Menu Item 3</a></li>
          <li><a href="#">Sub Menu Item 4</a></li>
        </ul>
      </li>
      <li class="menu-item slds-is-relative">
        <button aria-expanded="false" aria-haspopup="true">Menu Item 2<i class="fa-solid fa-chevron-down"></i></button>
        <ul class="sub-menu-items" aria-hidden="true">
          <li><a href="#">Sub Menu Item</a></li>
          <li><a href="#">Sub Menu Item 2</a></li>
          <li><a href="#">Sub Menu Item 3</a></li>
          <li><a href="#">Sub Menu Item 4</a></li>
        </ul>
      </li>
      <li class="menu-item slds-is-relative">
        <button aria-expanded="false" aria-haspopup="true">Tools <i class="fa-solid fa-chevron-down"></i></button>
        <ul class="sub-menu-items" aria-hidden="true">
          <li><a href="#">Sub Menu Item</a></li>
          <li><a href="#">Sub Menu Item 2</a></li>
          <li><a href="#">Sub Menu Item 3</a></li>
          <li><a href="#">Sub Menu Item 4</a></li>
        </ul>
      </li>
      <li class="menu-item slds-is-relative">
        <button aria-expanded="false" aria-haspopup="true">Contact [Name] <i class="fa-solid fa-chevron-down"></i></button>
        <ul class="sub-menu-items" aria-hidden="true">
          <li><a href="#">Sub Menu Item</a></li>
          <li><a href="#">Sub Menu Item 2</a></li>
          <li><a href="#">Sub Menu Item 3</a></li>
          <li><a href="#">Sub Menu Item 4</a></li>
        </ul>
      </li>
    </ul>
  </nav>
</header>

三、CSS微调(配合无障碍)

补上聚焦样式和细节优化,让键盘导航用户也能清晰感知:

.nav-menu-container {
  background: black;
  color: white;
  font-size: 0.875rem;
  padding: 1.3125rem 1.875rem;
}

.menu-item {
  display: inline-block; /* 改成inline-block,避免inline元素的定位bug */
  padding: 0 0.75rem;
  position: relative;
}

.menu-item button {
  background-color: transparent;
  border: none;
  color: white;
  cursor: pointer; /* 明确鼠标指针样式,告知用户可点击 */
  padding: 0.5rem 0;
}

/* 按钮聚焦样式,键盘导航必须可见 */
.menu-item button:focus {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

.sub-menu-items {
  background: white;
  display: none;
  left: -25px;
  min-width: 183px;
  padding: 10px 0;
  position: absolute;
  top: 40px;
}

.sub-menu-items.show {
  display: block;
}

.sub-menu-items:after {
  width: 0;
  height: 0;
  border-left: 15px solid transparent;
  border-right: 15px solid transparent;
  border-bottom: 15px solid white;
  content: " ";
  position: absolute;
  top: -10px;
  left: 42%;
}

.sub-menu-items a {
  color: black;
  text-decoration: none;
  padding: .5rem 1rem;
  display: block;
}

/* 链接也要加聚焦样式 */
.sub-menu-items a:hover,
.sub-menu-items a:focus {
  background-color: lightgray;
}

四、关键无障碍优化总结

  • 用aria-expanded替代aria-pressed:专门用于指示折叠控件的展开状态,语义更准确
  • 添加aria-haspopup="true":告诉屏幕阅读器这个按钮会弹出下拉菜单
  • aria-hidden控制子菜单可见性:隐藏时设为true,屏幕阅读器会忽略,避免干扰
  • 键盘支持:确保按钮可通过回车/空格触发,同时添加清晰的聚焦样式
  • 移除内联事件:避免HTML与JS耦合,维护更方便

关于ID的疑问

完全不需要用ID或者动态生成data-id,利用closest()和querySelector()的DOM层级遍历,就能精准定位到当前按钮对应的子菜单,代码更简洁,还能避免ID冲突的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 19:15:52