【问题标题】:Open menu on mouseover and Close menu on mouseleave in react在鼠标悬停时打开菜单并在鼠标离开时关闭菜单反应
【发布时间】:2017-12-28 20:24:11
【问题描述】:

我刚开始用 react 到处乱涂。我目前正在使用 material-ui 处理我的导航栏并做出反应。当我将鼠标悬停在菜单上时,会出现下拉菜单。但是为了关闭下拉菜单,我必须点击下拉菜单的外部。当我将鼠标悬停在下拉菜单之外或移动到不同的菜单选项(在这种情况下应该出现不同的下拉菜单)时,我希望能够关闭下拉菜单。像这样的:https://www.palantir.com/

我环顾四周,但没有找到解决方案。这是我得到的最接近的:Material-ui: open menu by event hover

我尝试使用相同的技术并将其添加到我的代码中,但无济于事。有什么建议么?谢谢!

编辑:我在这里重现了我的问题:https://react-xmaiyw.stackblitz.io 单击“为什么是我们”时可以看到问题。

 handleClick = (event) => {
 event.preventDefault();

   this.setState({
    open: true,
    anchorEl: event.currentTarget,
   });
 };

handleRequestClose = () => {
  this.setState({
   open: false,
  });
};

render() {
return (
  <FlatButton
  onClick={this.handleClick}
  onMouseOver={this.handleClick}
  onMouseLeave={this.handleRequestClose} //When I add this line of 
     //code, it keeps flickering very fast almost as if drop-down 
     //doesn't open
  label="Why Us?"
/>
)}

【问题讨论】:

  • 我不知道这是否是您正在寻找的答案,因为您似乎已经编写了很多 javascript,但这可以很简单地完成,无需使用 CSS 伪元素的 javascript hover。 W3Schools 有一个很好的教程,介绍如何在下拉菜单设置中实现这一点 here 如果这不是您想要的效果,请包含您的标记,以便我们更全面地了解您的目标。跨度>
  • 与 CSS 相比,我试图让它与 js 一起工作。我已经编辑了我的原始问题并为我的问题添加了一个工作链接。谢谢:)

标签: reactjs drop-down-menu material-ui onmouseover onmouseout


【解决方案1】:

闪烁是由鼠标下方的菜单打开引起的。当菜单打开时,鼠标不再位于按钮上方,因此它提示mouseleave 事件,关闭菜单,这样您的鼠标现在再次位于按钮上方,提示mouseenter 事件,打开菜单。 ..等等等等。

您可以使用一些额外的逻辑来跟踪鼠标的位置,并通过超时来确保用户有时间在按钮和菜单之间切换鼠标。

import React from 'react';
import Button from 'material-ui/Button';
import Menu, { MenuItem } from 'material-ui/Menu';

const timeoutLength = 300;

class SimpleMenu extends React.Component {
  state = {
    anchorEl: null,

    // Keep track of whether the mouse is over the button or menu
    mouseOverButton: false,
    mouseOverMenu: false,
  };

  handleClick = event => {
    this.setState({ open: true, anchorEl: event.currentTarget });
  };

  handleClose = () => {
    this.setState({ mouseOverButton: false, mouseOverMenu: false });
  };

  enterButton = () => {
    this.setState({ mouseOverButton: true });
  }

  leaveButton = () => {
    // Set a timeout so that the menu doesn't close before the user has time to
    // move their mouse over it
    setTimeout(() => {
      this.setState({ mouseOverButton: false });
    }, timeoutLength);
  }

  enterMenu = () => {
    this.setState({ mouseOverMenu: true });
  }

  leaveMenu = () => {
     setTimeout(() => {
      this.setState({ mouseOverMenu: false });
     }, timeoutLength);
  }

  render() {
    // Calculate open state based on mouse location
    const open = this.state.mouseOverButton || this.state.mouseOverMenu;

    return (
      <div>
        <Button
          aria-owns={this.state.open ? 'simple-menu' : null}
          aria-haspopup="true"
          onClick={this.handleClick}
          onMouseEnter={this.enterButton}
          onMouseLeave={this.leaveButton}
        >
          Open Menu
        </Button>
        <Menu
          id="simple-menu"
          anchorEl={this.state.anchorEl}
          open={open}
          onClose={this.handleClose}
          MenuListProps={{
            onMouseEnter: this.enterMenu,
            onMouseLeave: this.leaveMenu,
          }}

        >
          <MenuItem onClick={this.handleClose}>Profile</MenuItem>
          <MenuItem onClick={this.handleClose}>My account</MenuItem>
          <MenuItem onClick={this.handleClose}>Logout</MenuItem>
        </Menu>
      </div>
    );
  }
}

export default SimpleMenu;

我使用MenuListProps 直接在MenuList 本身上设置mouseEntermouseLeave 事件,因为Menu 组件包含一堆不可见的(disply: none) 过渡元素,它们对鼠标事件。 MenuList 是实际显示的元素,因此直接在其上设置鼠标事件是有意义的。

您可能需要使用timeoutLength 和过渡来让一切看起来顺利。

【讨论】:

    【解决方案2】:

    我遇到了同样的问题。 我解决了这样的问题。我将 LeaveMenu 事件分别给总组件和菜单组件,之后它完美地工作了

    import React from 'react';
    import {
      Menu,
      MenuItem as MuiMenuItem,
      Avatar,
      Divider,
      Typography,
      Switch,
      Fade,
    } from '@mui/material';
    import { useHistory } from 'react-router-dom';
    import { styled } from '@mui/styles';
    import { DarkMode as DarkModeIcon } from '@mui/icons-material';
    
    /********************  Styled Components  ********************/
    const UserAvatarButton = styled('div')(({ active, theme }) => ({
      height: 72,
      display: 'flex',
      justifyContent: 'center',
      alignItems: 'center',
      padding: '0px 20px',
      cursor: 'pointer',
      borderBottom: active ? `3px solid ${theme.palette.primary.main}` : 'none',
      borderRadius: 0,
    }));
    
    const ProfileMenuNavigation = styled(Menu)(() => ({
      '& .MuiList-root': {
        paddingTop: 0,
        paddingBottom: 0,
        minWidth: 220,
        maxWidth: 350,
      },
    }));
    
    const MenuItem = styled(MuiMenuItem)(({ theme }) => ({
      padding: 16,
      width: '100%',
      '&:hover': {
        backgroundColor: theme.palette.background.main,
        boxShadow: '5px 0px 5px 0px #888888',
        transition: 'box-shadow 0.3s ease-in-out',
      },
    }));
    
    const ProfileMenuText = styled(Typography)(() => ({
      fontFamily: 'Poppins',
      marginLeft: 16,
      marginRight: 16,
      fontSize: 16,
      fontWeight: 600,
    }));
    
    /********************  Main Component  ********************/
    const ProfileMenu = ({ menus, active }) => {
      const history = useHistory();
    
      const [anchorEl, setAnchorEl] = React.useState(null);
      const open = Boolean(anchorEl);
    
      const handleClick = (event) => {
        if (anchorEl) {
          setAnchorEl(null);
        } else {
          setAnchorEl(event.currentTarget);
        }
      };
    
      const handleClose = () => {
        setAnchorEl(null);
      };
    
      const goPath = (path) => {
        setAnchorEl(null);
        history.push(path);
      };
    
      const leaveMenu = () => {
        setTimeout(() => {
          setAnchorEl(null);
        }, 300);
      };
    
      return (
        <div onMouseLeave={leaveMenu}>
          <UserAvatarButton
            id="account-button"
            active={active}
            aria-controls={open ? 'account-menu' : undefined}
            aria-haspopup="true"
            aria-expanded={open ? 'true' : undefined}
            onClick={handleClick}
            onMouseOver={(event) => setAnchorEl(event.currentTarget)}
          >
            <Avatar
              sx={{
                width: 38,
                height: 38,
              }}
              alt="Avatar"
              src="https://i.pravatar.cc/300"
            />
          </UserAvatarButton>
          <ProfileMenuNavigation
            id="account-menu"
            anchorEl={anchorEl}
            open={open}
            onClose={handleClose}
            MenuListProps={{
              'aria-labelledby': 'account-button',
              onMouseLeave: leaveMenu,
            }}
            anchorOrigin={{
              vertical: 'bottom',
              horizontal: 'right',
            }}
            transformOrigin={{
              vertical: 'top',
              horizontal: 'right',
            }}
            TransitionComponent={Fade}
          >
            {menus.map((menu, index) => (
              <div key={index}>
                <MenuItem onClick={() => goPath(menu.path)}>
                  {menu?.icon}
                  <ProfileMenuText>{menu.text}</ProfileMenuText>
                </MenuItem>
                <Divider style={{ margin: 0 }} />
              </div>
            ))}
            <MenuItem onClick={() => {}}>
              <DarkModeIcon />
              <ProfileMenuText>Night Mode</ProfileMenuText>
              <div style={{ marginLeft: 16 }}>
                <Switch />
              </div>
            </MenuItem>
          </ProfileMenuNavigation>
        </div>
      );
    };
    
    export default ProfileMenu;
    

    【讨论】:

    • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
    猜你喜欢
    • 2021-02-20
    • 1970-01-01
    • 2021-04-28
    • 1970-01-01
    • 2011-06-22
    • 2016-01-28
    • 2019-02-20
    • 2022-10-25
    • 1970-01-01
    相关资源
    最近更新 更多