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

Vuetify v-menu键盘导航异常:回车触发子菜单开合不符合预期

解决Vuetify嵌套v-menu键盘回车二次关闭问题

使用键盘导航Vuetify嵌套v-menu时,首次按下回车可以打开子菜单,但再次按下回车会直接关闭该子菜单——即便子菜单下还有下级菜单,这个行为不符合预期。

问题原因

v-menu默认的激活器回车事件是**切换(toggle)**状态:第一次触发打开菜单,第二次触发关闭。但嵌套场景下,我们需要菜单打开后,回车操作应该聚焦到子菜单内容,而非关闭父菜单。

解决方案

重写激活器的回车事件逻辑,拦截默认的toggle行为:当菜单未打开时,回车触发打开;当菜单已打开时,跳过关闭动作,直接将焦点移至子菜单的第一个可交互元素。

修改后的核心代码如下:

<v-menu :close-on-content-click="false" :offset-x='isOffsetX' :offset-y='isOffsetY' :open-on-hover='isOpenOnHover' :transition='transition' :value="openMenu">
  <template v-slot:activator="{ on }">
    <v-btn v-if='icon' :color='color' @mousedown="on.mousedown" @keydown.enter="handleEnter">
      <v-icon>{{ icon }}</v-icon>
    </v-btn>
    <v-list-item v-else-if='isSubMenu' class='d-flex justify-space-between' @mousedown="on.mousedown" @keydown.enter="handleEnter">
      {{ name }}<div class="flex-grow-1"></div><v-icon>mdi-chevron-right</v-icon>
    </v-list-item>
    <v-btn v-else :color='color' @mousedown="on.mousedown" @keydown.enter="handleEnter" @click="openMenu = true" text >{{ name }}</v-btn>
  </template>
  <v-list ref="menuList">
    <template v-for="(item, index) in menuItems">
            <v-divider v-if='item.isDivider' :key='index' />
            <dan-menu v-else-if='item.menu' :key='index' :name='item.name' :menu-items='item.menu' @dan-menu-click='emitClickEvent' :is-open-on-hover=false :is-offset-x=true :is-offset-y=false :is-sub-menu=true />
            <v-list-item v-else :key='index' @click='emitClickEvent(item)'>
                <v-list-item-title>{{ item.name }}</v-list-item-title>
            </v-list-item>
    </template>
  </v-list>
</v-menu>

在组件的methods中添加事件处理函数:

methods: {
  handleEnter(e) {
    e.preventDefault(); // 阻止默认toggle行为
    if (!this.openMenu) {
      this.openMenu = true;
      // 菜单打开后延迟聚焦到子菜单第一个元素
      this.$nextTick(() => {
        const firstItem = this.$refs.menuList?.$el.querySelector('[tabindex="0"]');
        firstItem?.focus();
      });
    } else {
      // 菜单已打开时,直接聚焦到子菜单第一个元素
      const firstItem = this.$refs.menuList?.$el.querySelector('[tabindex="0"]');
      firstItem?.focus();
    }
  },
  emitClickEvent(item) {
    this.$emit('dan-menu-click', item);
  }
}

关键说明

  1. 替换v-on="on"为拆分的@mousedown="on.mousedown"和自定义回车事件,保留鼠标点击的默认行为,只修改键盘回车逻辑。
  2. 通过e.preventDefault()阻止v-menu默认的回车toggle动作。
  3. 菜单打开后,利用$nextTick确保DOM渲染完成,再聚焦到子菜单的第一个可交互元素,保证键盘导航的连贯性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 13:35:16