I've made a CSS grid displaying a <p>{name}</p>
element on each grid item. When an onMouseOver
hover events I want to display a new div (contained within the react fragment), but in the same position.
Instead of using CSS, I've used JavaScript to code the logic for this - when a user hovers on the grid item, it flips a boolean (saved as state variable isMouseHovering
) from false to true.
I have a conditional statement that displays the 'new' div, when this state value is truthy.
The problem is that this logic applies for grid item container element, and not for the elements inside the grid container. So when I over over grid, it does what it's supposed to do and displays the new div. However when I hover over the p-tag within the grid item container element, it flickers between states.
How do I structure this so that the div changes on hover, no matter where on the grid item the user hovers the mouse?
Project.js
import { useState } from 'react'
const Project = ({name,url,techStack,image,blurb}) => {
const [isMouseHovering, setMouseHovering] = useState(false);
const handleOnMouseOver = () => setMouseHovering((isMouseHovering) => !isMouseHovering);
const handleMouseOut = () => setMouseHovering(false);
return(
<div className = 'grid-item' onMouseOver={handleOnMouseOver} onMouseOut = {handleMouseOut}>
{
isMouseHovering ? (
// element I want to display when mouse hovering, in same position as <p>{name}</p> element
<>
<a href='https://xxxxxxxxxxxxxxxx'>
<p>website</p>
</a>
</>
)
:
<p>{name}</p>
}
</div>
)
}
export default Project
Try this:
import { useState } from 'react'
const Project = ({name,url,techStack,image,blurb}) => {
const handleOnMouseOver = (dir) => {
const ele = document.getElementById('dev_span');
if (dir === 'in' && ele) {
ele.style.display = 'block';
} else if (ele) {
ele.style.display = 'none';
}
};
return(
<div
className="grid-item"
onMouseEnter={() => handleOnMouseOver('in')}
onMouseLeave={() => handleOnMouseOver('out')}
>
<span id="dev_span" style={{ display: 'none' }}>
<a href="https://xxxxxxxxxxxxxxxx">
<p>website</p>
</a>
</span>
<p>{name}</p>
</div>
)
}
You can check for the element that is trigger the event.
import { useState } from "react";
const Project = ({ name, url, techStack, image, blurb }) => {
const [isMouseHovering, setMouseHovering] = useState(false);
const handleOnMouseOver = (e) => {
if (e.target.className === "grid-item") {
setMouseHovering((isMouseHovering) => !isMouseHovering);
}
};
const handleMouseOut = () => setMouseHovering(false);
return (
<div
className="grid-item"
onMouseOver={(e) => handleOnMouseOver(e)}
onMouseOut={handleMouseOut}
>
{isMouseHovering ? (
// element I want to display when mouse hovering, in same position as <p>{name}</p> element
<>
<a href="https://xxxxxxxxxxxxxxxx">
<p>website</p>
</a>
</>
) : (
<p>{name}</p>
)}
</div>
);
};
export default Project;
The issue is you're using onMouseOver and onMouseOut instead of onMouseEnter and onMouseLeave.
onMouseOut will trigger when you hover over a children of the element you are in causing the bug you're describing.
Also you're toggling the state in your handleMouseOver function but from what I understood you want it to be set to true when you hover the div.
import { useState } from 'react';
const Project = ({ name, url, techStack, image, blurb }) => {
const [isMouseHovering, setMouseHovering] = useState(false);
const handleMouseEnter = () => setMouseHovering(true);
const handleMouseLeave = () => setMouseHovering(false);
return (
<div
className="grid-item"
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
{isMouseHovering ? (
// element I want to display when mouse hovering, in same position as <p>{name}</p> element
<>
<a href="https://xxxxxxxxxxxxxxxx">
<p>website</p>
</a>
</>
) : (
<p>{name}</p>
)}
</div>
);
};
export default Project;
https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseenter_event