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

Angular Material 16悬浮菜单mouseleave事件异常触发求助

Angular Material 16 悬浮菜单mouseleave事件异常问题

我在使用Angular Material 16开发鼠标悬浮触发的菜单时,遇到了异常问题:鼠标刚进入按钮,mouseleave事件就被触发,导致菜单刚显示就立刻关闭。在buttonLeave方法中设置断点后,确认鼠标一进入按钮该方法就被调用,完全不符合“鼠标离开按钮时触发”的预期。

组件模板代码

<ng-container *ngFor="let menuItem of menuList">
  <ng-container *ngIf="!menuItem.children">
    <button
      class="nav-link"
      [routerLink]="menuItem.link"
      style="z-index: 1050;"
    >
      <span class="icon fa" [ngClass]="menuItem.icon"></span>
      <span class="text-holder">{{ menuItem.label }}</span>
    </button>
  </ng-container>
  <ng-container *ngIf="menuItem.children && menuItem.children.length > 0">
    <button
      #button
      mat-button
      [matMenuTriggerFor]="levelOne"
      #levelOneTrigger="matMenuTrigger"
      (mouseenter)="buttonEnter(levelOneTrigger)"
      (mouseleave)="buttonLeave(levelOneTrigger, button)"
      style="z-index:2000"
    >
      <span class="icon fa" [ngClass]="menuItem.icon"></span>
      <span
        >{{ menuItem.label }}
        <i class="fa fa-chevron-down"></i>
      </span>
    </button>

    <mat-menu
      #levelOne="matMenu"
      direction="down"
      yPosition="below"
      class="mnred1"
    >
      <span
        (mouseenter)="menuenter()"
        (mouseleave)="menuLeave(levelOneTrigger, button)"
        class="mnred2"
      >
        <ng-container *ngFor="let childL1 of menuItem.children">
          <li class="p-0" *ngIf="!childL1.children" mat-menu-item>
            <a class="nav-link"
              >{{ childL1.label }}
              <i *ngIf="childL1.icon" [ngClass]="childL1.icon"></i>
            </a>
          </li>
          <ng-container *ngIf="childL1.children && childL1.children.length > 0">
            <li
              mat-menu-item
              #levelTwoTrigger="matMenuTrigger"
              [matMenuTriggerFor]="levelTwo"
            >
              <span class="icon fa" [ngClass]="childL1.icon"></span>
              <span>{{ childL1.label }}</span>
            </li>

            <mat-menu #levelTwo="matMenu">
              <span
                (mouseenter)="menu2enter()"
                (mouseleave)="
                  menu2Leave(levelOneTrigger, levelTwoTrigger, button)
                "
              >
                <ng-container *ngFor="let childL2 of childL1.children">
                  <li class="p-0" mat-menu-item>
                    <a class="nav-link"
                      >{{ childL2.label }}
                      <i *ngIf="childL2.icon" [ngClass]="childL2.icon"></i>
                    </a>
                  </li>
                </ng-container>
              </span>
            </mat-menu>
          </ng-container>
        </ng-container>
      </span>
    </mat-menu>
  </ng-container>
</ng-container>

问题原因

核心问题出在MatMenu的渲染逻辑对鼠标事件的干扰:

  • 当mouseenter触发按钮打开菜单后,MatMenu会立即在按钮附近渲染菜单DOM,这个过程可能导致按钮元素发生微小的布局偏移,或者菜单元素覆盖了按钮的部分区域,让浏览器误判鼠标已经离开按钮,从而触发mouseleave事件。
  • 手动给按钮设置的z-index:2000打破了MatMenu默认的层级管理,菜单渲染时的层级冲突进一步干扰了鼠标事件的判定。

修复方案

1. 改用延迟关闭逻辑,避免误触发

通过跟踪鼠标是否在按钮或菜单区域,结合延迟判断来关闭菜单,减少误判:

  • 在组件类中添加状态变量:
isMouseInButton = false;
isMouseInLevelOneMenu = false;
isMouseInLevelTwoMenu = false;
  • 修改模板中的事件绑定:
<button
  #button
  mat-button
  [matMenuTriggerFor]="levelOne"
  #levelOneTrigger="matMenuTrigger"
  (mouseenter)="isMouseInButton = true; levelOneTrigger.openMenu()"
  (mouseleave)="isMouseInButton = false; closeLevelOneMenuDelayed(levelOneTrigger)"
>
  <!-- 按钮原有内容 -->
</button>

<mat-menu #levelOne="matMenu" direction="down" yPosition="below" class="mnred1">
  <span
    (mouseenter)="isMouseInLevelOneMenu = true"
    (mouseleave)="isMouseInLevelOneMenu = false; closeLevelOneMenuDelayed(levelOneTrigger)"
    class="mnred2"
  >
    <!-- 一级菜单原有内容 -->
    <mat-menu #levelTwo="matMenu">
      <span
        (mouseenter)="isMouseInLevelTwoMenu = true"
        (mouseleave)="isMouseInLevelTwoMenu = false; closeLevelTwoMenuDelayed(levelOneTrigger, levelTwoTrigger)"
      >
        <!-- 二级菜单原有内容 -->
      </span>
    </mat-menu>
  </span>
</mat-menu>
  • 添加延迟关闭方法:
closeLevelOneMenuDelayed(trigger: MatMenuTrigger) {
  setTimeout(() => {
    if (!this.isMouseInButton && !this.isMouseInLevelOneMenu && !this.isMouseInLevelTwoMenu) {
      trigger.closeMenu();
    }
  }, 200);
}

closeLevelTwoMenuDelayed(levelOneTrigger: MatMenuTrigger, levelTwoTrigger: MatMenuTrigger) {
  setTimeout(() => {
    if (!this.isMouseInLevelOneMenu && !this.isMouseInLevelTwoMenu) {
      levelTwoTrigger.closeMenu();
    }
    if (!this.isMouseInButton && !this.isMouseInLevelOneMenu && !this.isMouseInLevelTwoMenu) {
      levelOneTrigger.closeMenu();
    }
  }, 200);
}

2. 移除手动设置的z-index

删除按钮上的style="z-index:2000",让MatMenu自行管理菜单和按钮的层级关系,避免布局冲突。

3. 禁用菜单背景遮罩(可选)

如果不需要菜单的背景遮罩,可以给mat-menu添加[hasBackdrop]="false",减少背景元素对鼠标事件的干扰:

<mat-menu #levelOne="matMenu" direction="down" yPosition="below" class="mnred1" [hasBackdrop]="false">

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 11:54:56