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

React Material UI中如何通过onMouseOver&onMouseLeave控制菜单显隐

问题描述

我是React JS新手,希望实现鼠标悬停按钮时显示下拉菜单,无需点击按钮。我尝试了以下代码,但无法正常工作。

import * as React from 'react';
import Button from '@mui/material/Button';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import Fade from '@mui/material/Fade';
import { Container,Link } from '@mui/material';


function FadeMenu() {

const [anchorEl, setAnchorEl] = React.useState(null);
const [open, setOpen] = React.useState(false);

    const handleOver = (event) => {
        setAnchorEl(event.currentTarget);
setOpen(true);
    }

    const handleLeave = (event) => {
     
        setAnchorEl(null);
        setOpen(false);
    }

    const handleClose = (event) => {
        setAnchorEl(null);
    };

 return (

<Button
                id="about-fade-button"
                aria-controls="about-fade-menu"
                aria-haspopup="true"
                aria-expanded={open ? 'about-fade-menu' : 'false'}
                onMouseOver={handleOver}
                onMouseLeave={handleLeave}
                sx={{cursor:"pointer",textTransform:"none",fontSize:"14px",
                '&:hover':{color:"#30B54B",textDecoration:"underline",
            textDecorationThickness:"2px"},}}
            >
                About
            </Button>
            <Menu
                id="about-fade-menu"
                MenuListProps={{
                    'aria-labelledby': 'about-fade-button',
                }}
                anchorEl={anchorEl}
                open={open}
                onClose={handleClose}
                TransitionComponent={Fade}
            >
                <MenuItem onClick={handleClose}>1</MenuItem>
                <MenuItem onClick={handleClose}>2</MenuItem>
                <MenuItem onClick={handleClose}>3</MenuItem>
            </Menu>



            <Button
                id="helpcenter-fade-button"
                aria-controls="helpcenter-fade-menu"
                aria-haspopup="true"
                aria-expanded={open ? 'helpcenter-fade-menu' : 'false'}
                onMouseOver={handleOver}
                onMouseLeave={handleLeave}
                sx={{cursor:"pointer",textTransform:"none",fontSize:"14px",
                '&:hover':{color:"#30B54B",textDecoration:"underline",textDecorationStyle:"solid",
            textDecorationThickness:"2px"},}}
            >
                Help Center
            </Button>
            <Menu
                id="helpcenter-fade-menu"
                MenuListProps={{
                    'aria-labelledby': 'helpcenter-fade-button',
                }}
                anchorEl={anchorEl}
                open={open}
                onClose={handleClose}
                TransitionComponent={Fade}
            >
                <MenuItem onClick={handleClose}>4</MenuItem>
                <MenuItem onClick={handleClose}>5</MenuItem>
                <MenuItem onClick={handleClose}>6</MenuItem>
            </Menu>

);

}

export default FadeMenu;

我参考了示例,但示例仅展示了onMouseOver事件,我希望同时使用onMouseOver和onMouseLeave事件控制菜单的显示与隐藏。请指出我的代码错误并提供实现方案。


代码错误分析
  • 共享状态冲突:两个按钮和菜单共用同一个open和anchorEl状态,悬停任意一个按钮都会触发状态变更,导致两个菜单的显示逻辑混乱。
  • aria-expanded属性错误:该属性应该返回布尔值的字符串形式("true"或"false"),而非菜单ID,这会影响无障碍访问性。
  • 菜单鼠标交互缺失:仅给按钮绑定了onMouseLeave,当鼠标从按钮移到菜单上时,按钮的onMouseLeave会触发,直接关闭菜单,无法正常操作菜单选项。
  • handleClose函数不完整:仅清空了anchorEl,未更新open状态,导致菜单关闭逻辑不彻底。

修正实现方案

针对上述问题,我们为每个菜单单独维护状态,同时给菜单添加鼠标进入/离开事件,确保鼠标在菜单区域时菜单保持打开,只有当鼠标完全离开按钮+菜单区域时才关闭。

import * as React from 'react';
import Button from '@mui/material/Button';
import Menu from '@mui/material/Menu';
import MenuItem from '@mui/material/MenuItem';
import Fade from '@mui/material/Fade';

function FadeMenu() {
  // 为每个菜单单独维护状态,用对象存储不同菜单的锚点和打开状态
  const [menuStates, setMenuStates] = React.useState({
    about: { anchorEl: null, open: false },
    helpCenter: { anchorEl: null, open: false }
  });

  // 处理鼠标悬停按钮,打开对应菜单
  const handleButtonOver = (menuKey, event) => {
    setMenuStates(prev => ({
      ...prev,
      [menuKey]: { anchorEl: event.currentTarget, open: true }
    }));
  };

  // 鼠标离开按钮时延迟关闭,给鼠标移到菜单的缓冲时间
  const handleButtonLeave = (menuKey) => {
    setTimeout(() => {
      const currentState = menuStates[menuKey];
      if (currentState && currentState.open) {
        setMenuStates(prev => ({
          ...prev,
          [menuKey]: { ...currentState, open: false, anchorEl: null }
        }));
      }
    }, 100);
  };

  // 鼠标进入菜单时,保持菜单打开
  const handleMenuEnter = (menuKey) => {
    setMenuStates(prev => ({
      ...prev,
      [menuKey]: { ...prev[menuKey], open: true }
    }));
  };

  // 鼠标离开菜单时,关闭菜单
  const handleMenuLeave = (menuKey) => {
    setMenuStates(prev => ({
      ...prev,
      [menuKey]: { ...prev[menuKey], open: false, anchorEl: null }
    }));
  };

  // 点击菜单选项关闭对应菜单
  const handleMenuItemClick = (menuKey) => {
    setMenuStates(prev => ({
      ...prev,
      [menuKey]: { ...prev[menuKey], open: false, anchorEl: null }
    }));
  };

  return (
    <>
      <Button
        id="about-fade-button"
        aria-controls="about-fade-menu"
        aria-haspopup="true"
        aria-expanded={menuStates.about.open ? "true" : "false"}
        onMouseOver={(e) => handleButtonOver('about', e)}
        onMouseLeave={() => handleButtonLeave('about')}
        sx={{
          cursor: "pointer",
          textTransform: "none",
          fontSize: "14px",
          '&:hover': {
            color: "#30B54B",
            textDecoration: "underline",
            textDecorationThickness: "2px"
          },
          marginRight: 2
        }}
      >
        About
      </Button>
      <Menu
        id="about-fade-menu"
        MenuListProps={{
          'aria-labelledby': 'about-fade-button',
        }}
        anchorEl={menuStates.about.anchorEl}
        open={menuStates.about.open}
        onMouseEnter={() => handleMenuEnter('about')}
        onMouseLeave={() => handleMenuLeave('about')}
        TransitionComponent={Fade}
      >
        <MenuItem onClick={() => handleMenuItemClick('about')}>1</MenuItem>
        <MenuItem onClick={() => handleMenuItemClick('about')}>2</MenuItem>
        <MenuItem onClick={() => handleMenuItemClick('about')}>3</MenuItem>
      </Menu>

      <Button
        id="helpcenter-fade-button"
        aria-controls="helpcenter-fade-menu"
        aria-haspopup="true"
        aria-expanded={menuStates.helpCenter.open ? "true" : "false"}
        onMouseOver={(e) => handleButtonOver('helpCenter', e)}
        onMouseLeave={() => handleButtonLeave('helpCenter')}
        sx={{
          cursor: "pointer",
          textTransform: "none",
          fontSize: "14px",
          '&:hover': {
            color: "#30B54B",
            textDecoration: "underline",
            textDecorationStyle: "solid",
            textDecorationThickness: "2px"
          }
        }}
      >
        Help Center
      </Button>
      <Menu
        id="helpcenter-fade-menu"
        MenuListProps={{
          'aria-labelledby': 'helpcenter-fade-button',
        }}
        anchorEl={menuStates.helpCenter.anchorEl}
        open={menuStates.helpCenter.open}
        onMouseEnter={() => handleMenuEnter('helpCenter')}
        onMouseLeave={() => handleMenuLeave('helpCenter')}
        TransitionComponent={Fade}
      >
        <MenuItem onClick={() => handleMenuItemClick('helpCenter')}>4</MenuItem>
        <MenuItem onClick={() => handleMenuItemClick('helpCenter')}>5</MenuItem>
        <MenuItem onClick={() => handleMenuItemClick('helpCenter')}>6</MenuItem>
      </Menu>
    </>
  );
}

export default FadeMenu;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 02:02:08