paul23
paul23

Reputation: 9455

Material ui popover how to anchor to another element (different from event target)

Well typically when we use popover we set the anchor during a mouse event to event.currentTarget.

However this is impossible in certain situations and undesired in others. - How can I set the popover anchor element directly?

import React from "react";
import Popover from "@material-ui/core/Popover";
import Typography from "@material-ui/core/Typography";
import Button from "@material-ui/core/Button";

export default function SimplePopover() {
  const [anchorEl, setAnchorEl] = React.useState(null);

  function handleClick(event) {
    //setAnchorEl(event.currentTarget);
    setAnchorEl(); //How to refer to the div?
  }

  function handleClose() {
    setAnchorEl(null);
  }

  const open = Boolean(anchorEl);
  const id = open ? "simple-popover" : undefined;

  return (
    <div>
      <Typography>Anchor point of popover here</Typography>
      <Button aria-describedby={id} variant="contained" onClick={handleClick}>
        Open Popover
      </Button>
      <Popover
        id={id}
        open={open}
        anchorEl={anchorEl}
        onClose={handleClose}
        anchorOrigin={{
          vertical: "bottom",
          horizontal: "center"
        }}
        transformOrigin={{
          vertical: "top",
          horizontal: "center"
        }}
      >
        <Typography>The content of the Popover.</Typography>
      </Popover>
    </div>
  );
}

demo

Upvotes: 24

Views: 42472

Answers (2)

Ryan Cogswell
Ryan Cogswell

Reputation: 81146

You can use a ref to get at whichever element you want to use as the anchor. The example below uses spanRef to get at the element rendered by the Typography element.

The placement of the Popover relative to the anchor element is then dependent on the anchorOrigin and transformOrigin properties. In the example below, the top-left corner of the Popover will be aligned with the bottom-right corner of the span. I changed the component of the Typography from the default <p> element to <span> to make the positioning easier to see. Since block elements (such as <p>) have 100% width by default even if the contents don't take up much of that space, the Popover can appear to be far away from the anchor if the anchor element is much wider than is visually apparent.

import React from "react";
import Popover from "@material-ui/core/Popover";
import Typography from "@material-ui/core/Typography";
import Button from "@material-ui/core/Button";

export default function SimplePopover() {
  const [anchorEl, setAnchorEl] = React.useState(null);
  const spanRef = React.useRef();
  function handleClick() {
    setAnchorEl(spanRef.current);
  }

  function handleClose() {
    setAnchorEl(null);
  }

  const open = Boolean(anchorEl);
  const id = open ? "simple-popover" : undefined;

  return (
    <div>
      <Typography component="span" ref={spanRef}>
        Anchor point of popover here
      </Typography>
      <br />
      <Button aria-describedby={id} variant="contained" onClick={handleClick}>
        Open Popover
      </Button>
      <Popover
        id={id}
        open={open}
        anchorEl={anchorEl}
        onClose={handleClose}
        anchorOrigin={{
          vertical: "bottom",
          horizontal: "right"
        }}
        transformOrigin={{
          vertical: "top",
          horizontal: "left"
        }}
      >
        <Typography>The content of the Popover.</Typography>
      </Popover>
    </div>
  );
}

Edit Use ref for anchorEl

Upvotes: 32

This is a small variation from Ryan Cogswell example to open automatically the Popover:

https://codesandbox.io/s/use-ref-for-anchorel-forked-ou7eo

import React, { useEffect } from "react";
import Popover from "@material-ui/core/Popover";
import Typography from "@material-ui/core/Typography";
import Button from "@material-ui/core/Button";

export default function SimplePopover() {
  const [anchorEl, setAnchorEl] = React.useState(null);
  const divRef = React.useRef();
  function handleClick() {
    setAnchorEl(divRef.current);
  }

  function handleClose() {
    setAnchorEl(null);
  }

  useEffect(() => {
    setAnchorEl(divRef.current);
  }, [divRef]);

  const open = Boolean(anchorEl);
  const id = open ? "simple-popover" : undefined;

  return (
    <div>
      <Typography>Anchor point of popover here</Typography>
      <Button
        ref={divRef}
        aria-describedby={id}
        variant="contained"
        onClick={handleClick}
      >
        Open Popover
      </Button>
      <Popover
        id={id}
        open={open}
        anchorEl={anchorEl}
        onClose={handleClose}
        anchorOrigin={{
          vertical: "bottom",
          horizontal: "center"
        }}
        transformOrigin={{
          vertical: "top",
          horizontal: "center"
        }}
      >
        <Typography>The content of the Popover.</Typography>
      </Popover>
    </div>
  );
}

Upvotes: 1

Related Questions