简体   繁体   English

当我单击菜单以外的其他地方时如何关闭自定义菜单

[英]How to make a custom menu close when I click somewhere else other than the menu

To piggyback on a previous question , I'd like my menu to close whenever I click somewhere else other than the menu.为了解决上一个问题,我希望每当我单击菜单以外的其他地方时关闭我的菜单。

Currently, it will open/close when I click the "Hamburger Menu Button".目前,当我点击“汉堡菜单按钮”时,它会打开/关闭。 It will close if I click a link on the menu or the menu itself but I'd also like to close when I "blur" away from it.如果我单击菜单上的链接或菜单本身,它将关闭,但当我“模糊”离开它时,我也想关闭。 This would be like if you got an alert message and you clicked somewhere else, the alert would close.这就像如果您收到一条警报消息并单击其他地方,警报将关闭。

Here is the current CodeSandbox这是当前的CodeSandbox

The relevant code is in Header.tsx:相关代码在Header.tsx:

import React, { useState } from "react";
import { Link } from "react-router-dom";
import {
  AppBar,
  Container,
  createStyles,
  IconButton,
  makeStyles,
  Theme,
  Toolbar,
  Typography
} from "@material-ui/core";
import MenuIcon from "@material-ui/icons/Menu";
import clsx from "clsx";

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    root: {
      flexGrow: 1,
      "& a": {
        color: "white",
        textDecoration: "none"
      }
    },
    menuButton: {
      marginRight: theme.spacing(2),
      zIndex: 2
    },
    title: {
      flexGrow: 1,
      zIndex: 2
    },
    toolBar: {
      "& div": {
        transition: "left .1s"
      }
    },
    menu: {
      zIndex: 1,
      width: 200,
      height: "100%",
      position: "fixed",
      top: 48,
      transition: "left .1s",
      marginRight: theme.spacing(2),
      left: -200,
      background: "#3f51b5",
      "& div:first-element": {
        marginTop: 100
      }
    },
    menuOpen: {
      left: 0,
      transition: "left .1s"
    },
    menuClose: {
      left: -200,
      transition: "left .1s"
    },

    topMenu: {
      display: "flex",
      "& div": {
        marginLeft: theme.spacing(1)
      }
    }
  })
);
const UserMenu = () => {
  const classes = useStyles();
  const [menuOpen, setMenuOpen] = useState(false);
  const toggleMenu = () => setMenuOpen(!menuOpen);
  const handleMenuClick = () => toggleMenu();
  return (
    <>
      <IconButton
        edge="start"
        className={classes.menuButton}
        color="inherit"
        aria-label="menu"
        onClick={toggleMenu}
      >
        <MenuIcon />
      </IconButton>
      <div className={classes.toolBar}>
        <Container
          className={clsx(classes.menu, {
            [classes.menuOpen]: menuOpen,
            [classes.menuClose]: !menuOpen,
            [classes.toolBar]: true
          })}
          onClick={handleMenuClick}
        >
          <div>
            <Link to="#">My Profile</Link>
          </div>
          <div>
            <Link to="#">Account</Link>
          </div>
          <div>
            <Link to="#">Admin</Link>
          </div>
        </Container>
      </div>
    </>
  );
};

const Header: React.FC = ({ children }) => {
  const classes = useStyles();

  return (
    <AppBar position="static" className={classes.root}>
      <Toolbar variant="dense">
        <UserMenu />
        <Typography variant="h6" className={classes.title}>
          <Link to="#">Widgets, LLC</Link>
        </Typography>
        <div className={classes.topMenu}>
          <div>
            <Link to="#">Sign out</Link>
          </div>
          <div>
            <Link to="#">One more</Link>
          </div>
        </div>
      </Toolbar>
    </AppBar>
  );
};

export default Header;

**Edit: ** This is the current Sandbox after applying Ryan's changes and moving the UserMenu into its own file. **编辑:** 这是应用 Ryan 的更改并将UserMenu移动到其自己的文件后的当前沙盒

You can use Material-UI's ClickAwayListener for this.您可以为此使用 Material-UI 的ClickAwayListener The only tricky part is avoiding an immediate close of your menu after clicking on the button to open the menu (because of the click event being handled by the button opening the menu and then the same click event being handled by the ClickAwayListener closing the menu).唯一棘手的部分是避免在单击按钮打开菜单后立即关闭菜单(因为单击事件由打开菜单的按钮处理,然后由关闭菜单的 ClickAwayListener 处理相同的单击事件) . Typically you would want to avoid rendering the ClickAwayListener until the menu is open, but I think that might break the transition on the menu unless you did further changes.通常,您希望在菜单打开之前避免呈现ClickAwayListener ,但我认为这可能会中断菜单上的过渡,除非您进行进一步的更改。 My example addresses this problem by calling event.stopPropagation() in the click handler for the menu button ( handleOpen ).我的示例通过在菜单按钮 ( handleOpen ) 的点击处理程序中调用event.stopPropagation()来解决这个问题。

Here's a modified version of your code/sandbox demonstrating this:这是您的代码/沙箱的修改版本,演示了这一点:

import React, { useState } from "react";
import { Link } from "react-router-dom";
import {
  AppBar,
  Container,
  ClickAwayListener,
  createStyles,
  IconButton,
  makeStyles,
  Theme,
  Toolbar,
  Typography
} from "@material-ui/core";
import MenuIcon from "@material-ui/icons/Menu";
import clsx from "clsx";

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    root: {
      flexGrow: 1,
      "& a": {
        color: "white",
        textDecoration: "none"
      }
    },
    menuButton: {
      marginRight: theme.spacing(2),
      zIndex: 2
    },
    title: {
      flexGrow: 1,
      zIndex: 2
    },
    toolBar: {
      "& div": {
        transition: "left .1s"
      }
    },
    menu: {
      zIndex: 1,
      width: 200,
      height: "100%",
      position: "fixed",
      top: 48,
      transition: "left .1s",
      marginRight: theme.spacing(2),
      left: -200,
      background: "#3f51b5",
      "& div:first-element": {
        marginTop: 100
      }
    },
    menuOpen: {
      left: 0,
      transition: "left .1s"
    },
    menuClose: {
      left: -200,
      transition: "left .1s"
    },

    topMenu: {
      display: "flex",
      "& div": {
        marginLeft: theme.spacing(1)
      }
    }
  })
);
const UserMenu = () => {
  const classes = useStyles();
  const [menuOpen, setMenuOpen] = useState(false);
  const handleOpen = (event: React.MouseEvent) => {
    if (!menuOpen) {
      event.stopPropagation();
      setMenuOpen(true);
    }
  };
  const handleClose = (event: React.MouseEvent<any, MouseEvent>) => {
    if (menuOpen) {
      setMenuOpen(false);
    }
  };
  return (
    <>
      <IconButton
        edge="start"
        className={classes.menuButton}
        color="inherit"
        aria-label="menu"
        onClick={handleOpen}
      >
        <MenuIcon />
      </IconButton>
      <div className={classes.toolBar}>
        <ClickAwayListener onClickAway={handleClose}>
          <Container
            className={clsx(classes.menu, {
              [classes.menuOpen]: menuOpen,
              [classes.menuClose]: !menuOpen,
              [classes.toolBar]: true
            })}
            onClick={handleClose}
          >
            <div>
              <Link to="#">My Profile</Link>
            </div>
            <div>
              <Link to="#">Account</Link>
            </div>
            <div>
              <Link to="#">Admin</Link>
            </div>
          </Container>
        </ClickAwayListener>
      </div>
    </>
  );
};

const Header: React.FC = ({ children }) => {
  const classes = useStyles();

  return (
    <AppBar position="static" className={classes.root}>
      <Toolbar variant="dense">
        <UserMenu />
        <Typography variant="h6" className={classes.title}>
          <Link to="#">Widgets, LLC</Link>
        </Typography>
        <div className={classes.topMenu}>
          <div>
            <Link to="#">Sign out</Link>
          </div>
          <div>
            <Link to="#">One more</Link>
          </div>
        </div>
      </Toolbar>
    </AppBar>
  );
};

export default Header;

单击即可编辑关闭菜单

To piggyback on a previous question , I'd like my menu to close whenever I click somewhere else other than the menu.为了搭载上一个问题,我希望在单击菜单以外的其他位置时关闭菜单。

Currently, it will open/close when I click the "Hamburger Menu Button".目前,当我单击“汉堡菜单按钮”时,它将打开/关闭。 It will close if I click a link on the menu or the menu itself but I'd also like to close when I "blur" away from it.如果我单击菜单上的链接或菜单本身,它将关闭,但是当我“模糊”离开它时,我也想关闭它。 This would be like if you got an alert message and you clicked somewhere else, the alert would close.这就像如果您收到一条警报消息并单击其他地方,警报就会关闭。

Here is the current CodeSandbox这是当前的CodeSandbox

The relevant code is in Header.tsx:相关代码在 Header.tsx 中:

import React, { useState } from "react";
import { Link } from "react-router-dom";
import {
  AppBar,
  Container,
  createStyles,
  IconButton,
  makeStyles,
  Theme,
  Toolbar,
  Typography
} from "@material-ui/core";
import MenuIcon from "@material-ui/icons/Menu";
import clsx from "clsx";

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    root: {
      flexGrow: 1,
      "& a": {
        color: "white",
        textDecoration: "none"
      }
    },
    menuButton: {
      marginRight: theme.spacing(2),
      zIndex: 2
    },
    title: {
      flexGrow: 1,
      zIndex: 2
    },
    toolBar: {
      "& div": {
        transition: "left .1s"
      }
    },
    menu: {
      zIndex: 1,
      width: 200,
      height: "100%",
      position: "fixed",
      top: 48,
      transition: "left .1s",
      marginRight: theme.spacing(2),
      left: -200,
      background: "#3f51b5",
      "& div:first-element": {
        marginTop: 100
      }
    },
    menuOpen: {
      left: 0,
      transition: "left .1s"
    },
    menuClose: {
      left: -200,
      transition: "left .1s"
    },

    topMenu: {
      display: "flex",
      "& div": {
        marginLeft: theme.spacing(1)
      }
    }
  })
);
const UserMenu = () => {
  const classes = useStyles();
  const [menuOpen, setMenuOpen] = useState(false);
  const toggleMenu = () => setMenuOpen(!menuOpen);
  const handleMenuClick = () => toggleMenu();
  return (
    <>
      <IconButton
        edge="start"
        className={classes.menuButton}
        color="inherit"
        aria-label="menu"
        onClick={toggleMenu}
      >
        <MenuIcon />
      </IconButton>
      <div className={classes.toolBar}>
        <Container
          className={clsx(classes.menu, {
            [classes.menuOpen]: menuOpen,
            [classes.menuClose]: !menuOpen,
            [classes.toolBar]: true
          })}
          onClick={handleMenuClick}
        >
          <div>
            <Link to="#">My Profile</Link>
          </div>
          <div>
            <Link to="#">Account</Link>
          </div>
          <div>
            <Link to="#">Admin</Link>
          </div>
        </Container>
      </div>
    </>
  );
};

const Header: React.FC = ({ children }) => {
  const classes = useStyles();

  return (
    <AppBar position="static" className={classes.root}>
      <Toolbar variant="dense">
        <UserMenu />
        <Typography variant="h6" className={classes.title}>
          <Link to="#">Widgets, LLC</Link>
        </Typography>
        <div className={classes.topMenu}>
          <div>
            <Link to="#">Sign out</Link>
          </div>
          <div>
            <Link to="#">One more</Link>
          </div>
        </div>
      </Toolbar>
    </AppBar>
  );
};

export default Header;

**Edit: ** This is the current Sandbox after applying Ryan's changes and moving the UserMenu into its own file. **编辑:** 这是应用 Ryan 的更改并将UserMenu移动到其自己的文件后的当前沙盒

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM