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

如何找到子导航中的最后一个可聚焦元素以实现Tab键导航跳转?

如何定位子导航的最后一个可聚焦元素,实现Tab键跳转主导航的功能

先明确需求:我们有四个主导航项(Shirts、Pants、Footwear、Headgear),点击每个项会展开对应的子导航菜单;主导航类名是.mainNav,子导航类名是.subNav-0这类(建议统一加.subNav类方便选择)。要求用户用Tab键遍历到子导航最后一个元素时,自动关闭当前子导航,同时把焦点跳到下一个主导航项——比如在Shirts子导航的Z Brand上按Tab,就关闭Shirts子导航并聚焦到Pants。

第一步:明确可聚焦元素范围

默认能被Tab键聚焦的元素包括:

  • 带href属性的<a>标签
  • <button>、<input>、<select>、<textarea>
  • 设置了tabindex≥0的元素

注意:你提供的HTML里<a>标签写法有误,应该写成<a href="#">...</a>,不然不带href的<a>默认是不可聚焦的,除非手动加tabindex。

第二步:获取子导航的最后一个可聚焦元素

针对当前展开的子导航,用选择器筛选出所有可聚焦元素,再取最后一个即可:

// 假设当前展开的子导航元素是subNavEl
const focusableElements = subNavEl.querySelectorAll('a[href], button, input, select, textarea, [tabindex]:not([tabindex="-1"])');
const lastFocusableEl = focusableElements[focusableElements.length - 1];

第三步:监听Tab键事件,实现跳转逻辑

给最后一个可聚焦元素绑定keydown事件,判断用户是否按了Tab键(且没按Shift,Shift+Tab是往回跳,不需要处理),然后执行关闭子导航、聚焦下一个主导航的操作:

lastFocusableEl.addEventListener('keydown', (e) => {
  if (e.key === 'Tab' && !e.shiftKey) {
    e.preventDefault(); // 阻止默认的Tab跳转行为
    // 关闭当前子导航
    subNavEl.style.display = 'none';
    // 找到当前主导航项的下一个兄弟元素
    const currentMainItem = document.querySelector('.mainNav a:focus');
    let nextMainItem = currentMainItem.nextElementSibling;
    // 如果是最后一个主导航,跳回第一个
    if (!nextMainItem) {
      nextMainItem = document.querySelector('.mainNav a:first-child');
    }
    nextMainItem.focus(); // 聚焦到下一个主导航
  }
});

完整示例代码

修正后的HTML(修复<a>标签错误,统一子导航类名)

<div class="mainNav">
  <a href="#">Shirts</a>
  <a href="#">Pants</a>
  <a href="#">Footwear</a>
  <a href="#">Headgear</a>
</div>

<div class="subNav subNav-0" style="display: none;">
  <h1>Shirts</h1>
  <a href="#">ABC Brand</a>
  <a href="#">Guess</a>
  <a href="#">Levis</a>
  <a href="#">Z Brand</a>
</div>

<div class="subNav subNav-1" style="display: none;">
  <h1>Pants</h1>
  <a href="#">ABC Brand</a>
  <a href="#">Guess</a>
  <a href="#">Levis</a>
  <a href="#">Z Brand</a>
</div>

<div class="subNav subNav-2" style="display: none;">
  <h1>Headgear</h1>
  <a href="#">Big Caps</a>
  <a href="#">Levis</a>
  <a href="#">Pacific Hatwear</a>
  <a href="#">Polo</a>
  <a href="#">Stetson</a>
</div>

<div class="subNav subNav-3" style="display: none;">
  <h1>Footwear</h1>
  <a href="#">Adidas</a>
  <a href="#">Asics</a>
  <a href="#">Brooks</a>
  <a href="#">Converse</a>
  <a href="#">New Balance</a>
  <a href="#">Nike</a>
  <a href="#">Puma</a>
  <a href="#">Saucony</a>
</div>

对应的JavaScript

// 绑定主导航点击事件,控制子导航显示隐藏
const mainNavItems = document.querySelectorAll('.mainNav a');
const subNavs = document.querySelectorAll('.subNav');

mainNavItems.forEach((item, index) => {
  item.addEventListener('click', (e) => {
    e.preventDefault();
    // 先隐藏所有子导航
    subNavs.forEach(sub => sub.style.display = 'none');
    // 显示当前对应的子导航
    const targetSubNav = document.querySelector(`.subNav-${index}`);
    targetSubNav.style.display = 'block';
    // 给当前子导航的最后可聚焦元素绑定Tab事件
    setupTabNavigation(targetSubNav, item);
  });
});

// 封装Tab导航处理逻辑
function setupTabNavigation(subNavEl, currentMainItem) {
  // 移除之前绑定的事件,避免重复绑定
  const prevLastElSelector = subNavEl.dataset.lastFocusEl;
  if (prevLastElSelector) {
    const prevLastEl = document.querySelector(prevLastElSelector);
    if (prevLastEl) {
      prevLastEl.removeEventListener('keydown', handleTab);
    }
  }

  // 获取子导航内所有可聚焦元素
  const focusableElements = subNavEl.querySelectorAll('a[href], button, input, select, textarea, [tabindex]:not([tabindex="-1"])');
  if (focusableElements.length === 0) return;

  const lastFocusableEl = focusableElements[focusableElements.length - 1];
  // 存储元素选择器,方便后续移除事件
  subNavEl.dataset.lastFocusEl = lastFocusableEl.tagName === 'A' ? `a[href="${lastFocusableEl.getAttribute('href')}"]` : `#${lastFocusableEl.id}`;
  
  // 绑定Tab处理函数
  lastFocusableEl.addEventListener('keydown', handleTab);

  function handleTab(e) {
    if (e.key === 'Tab' && !e.shiftKey) {
      e.preventDefault();
      // 关闭子导航
      subNavEl.style.display = 'none';
      // 找到下一个主导航项
      let nextMainItem = currentMainItem.nextElementSibling;
      // 循环到第一个主导航
      if (!nextMainItem) {
        nextMainItem = document.querySelector('.mainNav a:first-child');
      }
      nextMainItem.focus();
    }
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 15:50:25