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

如何在MUI Button上同时触发onMouseEnter与onClick?问题求助

解决MUI Button悬停下拉菜单时点击事件失效的问题

我需要实现头部登录按钮的两个交互:鼠标悬停显示下拉菜单,点击显示登录弹窗。使用MUI Button组件后,出现点击事件不触发、按钮无手型指针的问题,仅悬停事件生效。

问题原因

  1. 下拉菜单(Menu)打开后覆盖在Button上方,拦截了点击事件,导致Button无法响应点击
  2. 未处理鼠标从Button移到Menu时的快速关闭问题,干扰交互流程
  3. Button的指针样式未明确设置,或因Menu层级问题导致pointer-events被禁用

方案一:优化MUI组件实现

调整事件处理逻辑,确保Button始终能接收点击,同时优化菜单的显示/关闭时机:

完整代码

import { useState, useRef } from 'react';
import { Button, Menu, MenuItem } from '@mui/material';
import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward';
import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward';

const HeaderLoginButton = () => {
  const [openMenu, setOpenMenu] = useState(false);
  const [anchorEl, setAnchorEl] = useState(null);
  const [showLoginModal, setShowLoginModal] = useState(false);
  const [signOpen, setSignOpen] = useState(false);
  const menuTimer = useRef(null);

  // 鼠标进入按钮:打开菜单
  const handleMouseEnter = (event) => {
    clearTimeout(menuTimer.current);
    setAnchorEl(event.currentTarget);
    setOpenMenu(true);
  };

  // 鼠标离开按钮:延迟关闭菜单
  const handleMouseLeave = () => {
    menuTimer.current = setTimeout(() => {
      setOpenMenu(false);
    }, 200);
  };

  // 鼠标进入菜单:清除关闭计时器
  const handleMenuMouseEnter = () => {
    clearTimeout(menuTimer.current);
  };

  // 鼠标离开菜单:延迟关闭菜单
  const handleMenuMouseLeave = () => {
    menuTimer.current = setTimeout(() => {
      setOpenMenu(false);
    }, 200);
  };

  // 按钮点击:关闭菜单 + 打开登录弹窗
  const handleButtonClick = () => {
    setOpenMenu(false);
    setShowLoginModal(true);
    console.log("hi");
  };

  return (
    <>
      <Button
        onMouseEnter={handleMouseEnter}
        onMouseLeave={handleMouseLeave}
        onClick={handleButtonClick}
        endIcon={!openMenu ? <ArrowDownwardIcon /> : <ArrowUpwardIcon />}
        style={{ 
          color: "#047BD5", 
          background: "white",
          cursor: "pointer"
        }} 
        variant="contained"
      >
        Login
      </Button>
      <Menu
        id="basic-menu"
        open={openMenu}
        anchorEl={anchorEl}
        onClose={() => setOpenMenu(false)}
        style={{ width: "100%" }}
        MenuListProps={{
          'aria-labelledby': 'basic-button',
          onMouseEnter: handleMenuMouseEnter,
          onMouseLeave: handleMenuMouseLeave
        }}
      >
        {/* 修正Auth逻辑:未登录显示Login,已登录显示Logout */}
        {!Auth && (
          <MenuItem 
            style={{ color: "#047BD5" }} 
            onClick={() => {
              setShowLoginModal(true);
              setOpenMenu(false);
            }}
          >
            Login
          </MenuItem>
        )}
        <MenuItem 
          style={{ color: "#047BD5" }} 
          onClick={() => {
            setSignOpen(true);
            setOpenMenu(false);
          }}
        >
          SignUp
        </MenuItem>
        <MenuItem style={{ color: "#047BD5" }} onClick={() => setOpenMenu(false)}>Orders</MenuItem>
        <MenuItem style={{ color: "#047BD5" }} onClick={() => setOpenMenu(false)}>Whislist</MenuItem>
        {Auth && (
          <MenuItem 
            style={{ color: "#047BD5" }} 
            onClick={() => {
              // 添加Logout逻辑
              setOpenMenu(false);
            }}
          >
            Logout
          </MenuItem>
        )}
      </Menu>
      {/* {showLoginModal && <LoginModal onClose={() => setShowLoginModal(false)} />} */}
    </>
  );
};

export default HeaderLoginButton;

关键优化点

  • 给Button添加cursor: pointer样式,确保手型指针始终显示
  • 使用计时器延迟关闭菜单,避免鼠标从Button移到Menu时误关
  • 点击Button时强制关闭菜单并触发登录弹窗,避免菜单拦截点击事件
  • 联动菜单的鼠标进入/离开事件,保持菜单在用户操作时的显示状态
  • 修正原代码中Auth的逻辑错误,确保登录/登出选项正确显示

方案二:原生按钮实现(无MUI依赖)

如果不想使用MUI组件,也可以用原生HTML元素实现相同效果:

完整代码

import { useState, useRef } from 'react';
import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward';
import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward';

const HeaderLoginButton = () => {
  const [openMenu, setOpenMenu] = useState(false);
  const [showLoginModal, setShowLoginModal] = useState(false);
  const [signOpen, setSignOpen] = useState(false);
  const menuTimer = useRef(null);

  const handleMouseEnter = () => {
    clearTimeout(menuTimer.current);
    setOpenMenu(true);
  };

  const handleMouseLeave = () => {
    menuTimer.current = setTimeout(() => {
      setOpenMenu(false);
    }, 200);
  };

  const handleMenuMouseEnter = () => {
    clearTimeout(menuTimer.current);
  };

  const handleMenuMouseLeave = () => {
    menuTimer.current = setTimeout(() => {
      setOpenMenu(false);
    }, 200);
  };

  const handleButtonClick = () => {
    setOpenMenu(false);
    setShowLoginModal(true);
    console.log("hi");
  };

  return (
    <div style={{ position: "relative", display: "inline-block" }}>
      <button
        onMouseEnter={handleMouseEnter}
        onMouseLeave={handleMouseLeave}
        onClick={handleButtonClick}
        style={{
          color: "#047BD5",
          backgroundColor: "white",
          border: "1px solid #047BD5",
          padding: "8px 16px",
          borderRadius: "4px",
          cursor: "pointer",
          display: "flex",
          alignItems: "center",
          gap: "8px",
          outline: "none"
        }}
      >
        Login
        {!openMenu ? <ArrowDownwardIcon fontSize="small" /> : <ArrowUpwardIcon fontSize="small" />}
      </button>
      {openMenu && (
        <div
          onMouseEnter={handleMenuMouseEnter}
          onMouseLeave={handleMenuMouseLeave}
          style={{
            position: "absolute",
            top: "100%",
            left: 0,
            width: "100%",
            backgroundColor: "white",
            border: "1px solid #eee",
            borderRadius: "4px",
            boxShadow: "0 2px 8px rgba(0,0,0,0.1)",
            marginTop: "4px"
          }}
        >
          {!Auth && (
            <div 
              onClick={() => {
                setShowLoginModal(true);
                setOpenMenu(false);
              }} 
              style={{ 
                padding: "8px 16px", 
                color: "#047BD5", 
                cursor: "pointer",
                transition: "background 0.2s"
              }}
              onMouseEnter={(e) => e.target.style.background = "#f5f5f5"}
              onMouseLeave={(e) => e.target.style.background = "white"}
            >
              Login
            </div>
          )}
          <div 
            onClick={() => {
              setSignOpen(true);
              setOpenMenu(false);
            }} 
            style={{ 
              padding: "8px 16px", 
              color: "#047BD5", 
              cursor: "pointer",
              transition: "background 0.2s"
            }}
            onMouseEnter={(e) => e.target.style.background = "#f5f5f5"}
            onMouseLeave={(e) => e.target.style.background = "white"}
          >
            SignUp
          </div>
          <div 
            onClick={() => setOpenMenu(false)} 
            style={{ 
              padding: "8px 16px", 
              color: "#047BD5", 
              cursor: "pointer",
              transition: "background 0.2s"
            }}
            onMouseEnter={(e) => e.target.style.background = "#f5f5f5"}
            onMouseLeave={(e) => e.target.style.background = "white"}
          >
            Orders
          </div>
          <div 
            onClick={() => setOpenMenu(false)} 
            style={{ 
              padding: "8px 16px", 
              color: "#047BD5", 
              cursor: "pointer",
              transition: "background 0.2s"
            }}
            onMouseEnter={(e) => e.target.style.background = "#f5f5f5"}
            onMouseLeave={(e) => e.target.style.background = "white"}
          >
            Whislist
          </div>
          {Auth && (
            <div 
              onClick={() => {
                // 添加Logout逻辑
                setOpenMenu(false);
              }} 
              style={{ 
                padding: "8px 16px", 
                color: "#047BD5", 
                cursor: "pointer",
                transition: "background 0.2s"
              }}
              onMouseEnter={(e) => e.target.style.background = "#f5f5f5"}
              onMouseLeave={(e) => e.target.style.background = "white"}
            >
              Logout
            </div>
          )}
        </div>
      )}
      {/* {showLoginModal && <LoginModal onClose={() => setShowLoginModal(false)} />} */}
    </div>
  );
};

export default HeaderLoginButton;

原生方案优势

  • 完全自定义样式,避免MUI组件的样式冲突
  • 事件逻辑更直接,减少框架层的事件拦截问题
  • 无需依赖MUI库,降低项目体积

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 19:15:15