robin-mbg
robin-mbg

Reputation: 213

Material UI: Display sub-element on hover of parent

When the user hovers over a Card component, I'd like to show a button on that component that is otherwise invisible. In CSS, I'd do something like this:

.card:hover my-button {
  display: block;
}

How do I replicate this in the "Material-UI" way?

All the Material-UI tips I found so far suggest something like this to add hover styling, but this applies the styles to the component that is being hovered over and not a different one.

  '&:hover': {
    background: 'blue'
  }

Upvotes: 12

Views: 21818

Answers (6)

Obada ElSharbatly
Obada ElSharbatly

Reputation: 76

I faced this problem today and after I discussed it with my mentor I made this result and it worked well for me. but first, let me tell you the disadvantage of using eventListener like onMouseEnter & on MouseLeave that it will cause so many renders.

I gave the (parent component) a movie-card class and the (child component) a movie-card-content class like the following

// movie-card.css
.movie-card-content {
  opacity: 0;
}

.movie-card:hover .movie-card-content {
  opacity: 1;
}

MUI allows you to add className prop so I gave the proper classNames

//MovieCard.jsx (component)
import "./movie-card.css";

function MovieCard () {

  return (
    <Card className="movie-card">
      <CardContent className="movie-card-content">...<CardContent>
    </Card>
  );
}

and that's it 😉

Upvotes: 3

Ois&#237;n Foley
Ois&#237;n Foley

Reputation: 3467

If you want to define this purely inside of a styled component instead of using either of createMuiTheme or makeStyles, then try the following.
We will give an id to each child component and reference them when defining the behaviour to implement when we hover over the parent component:

const NameCellBox = styled(Box)(({ theme }) => ({
    ...other styles,
    "&:hover #cellBoxLengthTypo": {
        display: "none",
    },
    "&:hover #cellBoxContentTypo": {
        display: "inline-block",
    },
}));

const CellBoxLengthTypo = styled(Typography)(({ theme }) => ({
    fontFamily: theme.typography.fontFamily,
    fontSize: theme.typography.h5.fontSize,
}));

const CellBoxContentTypo = styled(Typography)(({ theme }) => ({
    display: "none",
    fontFamily: theme.typography.fontFamily,
    fontSize: theme.typography.h5.fontSize,
}));
const items: string[] = ["andrew", "barry", "chris", "debbie"];
return (
    <>
        <NameCellBox>
            <CellBoxLengthTypo id="cellBoxLengthTypo">+{items.length}</CellBoxLengthTypo>
            <CellBoxContentTypo id="cellBoxContentTypo">{items.join(", ")}</CellBoxContentTypo>
        </NameCellBox>
    </>
);

Found it useful for updating a DataGrid cell in Material UI when hover or other event fired.

Upvotes: 1

Dotun
Dotun

Reputation: 11

This is a material UI example that displays the sub-element on hover of the parent. I also noticed that using some of the examples above, when using Material UI's makeStyles, the overlay item was flickering a lot when clicked. This solution below does not flicker when sub-element is clicked.

import React from "react"
import { Card, CardActionArea, CardContent, CardMedia } from "@material- 
       ui/core";
import { makeStyles } from "@material-ui/core/styles";

const useStyles = makeStyles(theme => ({
    card: {
        // some styles
    },
    cardAction: {
        position: "relative"
    },
    media: {
        // some styles
    },
    overlay: {
        position: "absolute",
        top: "85px"
    }
}));

const App = () => {
    const classes = useStyles();
    const [show, setShow] = React.useState(false);
    const handleMouseOver = () => {
        setShow(true);
    };
    const handleMouseOut = () => {
        setShow(false);
    };
    return (
      <Card>
          <CardActionArea
          onMouseOver={handleMouseOver}
          onMouseOut={handleMouseOut} className={classes.card} >
              <CardMedia className={classes.media} component="img" >
              // some content
              </CardMedia>
              <CardContent className={classes.overlay} style={{ display: show ? 
               'block' : 'none' }>
               // some content
              </CardContent>
           </CardActionArea>
      </Card>
  );
}

Upvotes: 1

Tchakoumi Lorrain
Tchakoumi Lorrain

Reputation: 21

import {
  makeStyles
} from '@material-ui/core'

const useStyles = makeStyles(() => ({
  root: {
    "& .appear-item": {
      display: "none"
    },
    "&:hover .appear-item": {
      display: "block"
    }
  }
}))

export default function MakeTextAppearOnHover() {
  const classes = useStyles()
  return (
    <div className = { classes.root }>
      Hello world
      <span className = 'appear-item' >
        Appearing text Go
      </span>
    </div>
  )
}

Upvotes: 2

Dekel
Dekel

Reputation: 62676

You can do the exact same thing with CSS using the createMuiTheme:

export const theme = createMuiTheme({
  overrides: {
    // For label
    MuiCard: {
      root: {
        "& .hidden-button": {
          display: "none"
        },
        "&:hover .hidden-button": {
          display: "flex"
        }
      }
    }
  }
});

Give the Button inside your Card the className hidden-button and you will get the same thing that you want.

Check it here: https://codesandbox.io/s/mui-theme-css-hover-example-n8ou5

Upvotes: 13

Zohaib Ijaz
Zohaib Ijaz

Reputation: 22935

It is not specific to Material UI but a react specific thing. you need a state variable to show/hide your button.

const App = () => {
  const [show, setShow] = useState(false);
  return (
    <Card
      onMouseOver={() => setShow(true)}
      onMouseOut={() => setShow(false)}>
      <CardBody>
        // some content
        {show && <Button>Click</Button>}
      </CardBody>
    </Card>
  );

}

Upvotes: 9

Related Questions