Change src of image on hover with array property - javascript

I'm mapping through an array that contains properties such as image: link to image and hover_image: link to image
Is there a way to change an image on hover? Something sort of like this:
<img src={hover ? item.hover_image : item.image} />
If so, how can I define that the element is being hovered over, thank you!
Update: the onMouseHover or any other onMouse seems to not work on my image tag, here is the code:
{array.map((item, key) => {
<div key={key}>
<a target="_blank" href={item.link} >
<img
onMouseEnter={(e) => console.log(e.currentTarget.src)}
alt={item.name}
src={item.image}
/>
</a>
</div>
})}

React has two event handlers for that:
onMouseEnter
onMouseLeave
You could put those events on the component that you are hovering over and make them set a state variable, to track the hover state. e.g.:
function YourOuterComponent({item}) {
const [hover, setHover] = useState(null);
return (
<>
<div id='whatever'
onMouseEnter={(e) => setHover(e.currentTarget.id)}
onMouseLeave={() => setHover(null)}>
hover over me
</div>
<img src={hover ? item.hover_image : item.image} />
</>
);
}
Edit based on coments:
{array.map((item, key) => (
<div key={key}>
<a target="_blank" href={item.link}>
<img
alt={item.name}
src={item.image}
onMouseEnter={(e) => {
e.currentTarget.src = item.hover_image;
}}
onMouseLeave={(e) => {
e.currentTarget.src = item.image;
}}
/>
</a>
</div>
))}

Inline JavaScript version**
<img src="a.jpg" onmouseover="this.src='b.jpg'" onmouseout="this.src='a.jpg'" />
this refer to the current img tag
Example :
<img width="100" height="100" src="https://onlinejpgtools.com/images/examples-onlinejpgtools/coffee-resized.jpg" onmouseover="this.src='https://cdn.pixabay.com/photo/2021/08/25/20/42/field-6574455__340.jpg'" onmouseout="this.src='https://onlinejpgtools.com/images/examples-onlinejpgtools/coffee-resized.jpg'" />

You can achieve this behaviour by changing the src property of the target on which the event has occurred.
For an example:
function App() {
const imgs = [
{
image:
"https://images.unsplash.com/photo-1644982654072-0b42e6636821?ixlib=rb-1.2.1&ixid=MnwxMjA3fDF8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1470&q=80",
hover_image:
"https://images.unsplash.com/photo-1648655514581-12808a5eb8c3?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwzfHx8ZW58MHx8fHw%3D&auto=format&fit=crop&w=500&q=60",
},
];
const elements = imgs.map((img, i) => {
return (
<img
key={i}
onMouseOver={(e) => {
e.target.src = img.hover_image;
}}
src={img.image}
style={{ height: "200px", width: "200px" }}
/>
);
});
return <div className="app">{elements}</div>;
}

Related

Conditional styling in react map

I only want to show the display block on the hovered item. but when I hover it shows on every item in a map function. what I'm doing wrong.
basically, I just want to show hovered movie item's title. for now, it shows every movie when I hover.
MovieList.js
const [popular, setPopular] = useState([]);
const [hover, setHover] = useState(false);
return (
<>
<div className="movie-list">
<h2 style={{ fontSize: "49px", marginLeft: "60px" }}>What's Popular</h2>
<div className="popular">
{popular.map((pop, index) => (
<>
<div className="movie" key={index}>
<div className="tot" onMouseEnter={() => setHover(true)}>
<h4
id="pop-title"
style={{ display: hover ? "block" : "none" }}
key={index}
>
{pop.title}
</h4>
</div>
<img
src={"https://image.tmdb.org/t/p/w500" + pop.poster_path}
id="movie-img"
/>
</div>
</>
))}
</div>
</div>
</>
);
};
export default MovieList;
The same hover state variable is used for all your movies, so when it becomes true, all the movies are affected by the change. You could use something like an object instead of just a boolean to store one hover state per movie.
Another problem with your code that isn't helping:
You are missing a unique key prop on each item of the map (it must be on the direct child).
Solution 1: Remove the Fragment
Everything is already under one div so you don't need the React Fragment (<>) in that case. Also, you might wanna use something unique to the current map item other than the index in the array.
{popular.map((pop) => (
<div className="movie" key={pop.somethingUnique}>
<div className="tot" onMouseEnter={() => setHover(true)}>
<h4 id="pop-title" style={{ display: hover ? "block" : "none" }}>
{pop.title}
</h4>
</div>
<img
src={"https://image.tmdb.org/t/p/w500" + pop.poster_path}
id="movie-img"
/>
</div>
))}
Solution 2: Set the key on the Fragment
{popular.map((pop) => (
<Fragment key={pop.somethingUnique}>
<div className="movie">
<div className="tot" onMouseEnter={() => setHover(true)}>
<h4 id="pop-title" style={{ display: hover ? "block" : "none" }}>
{pop.title}
</h4>
</div>
<img
src={"https://image.tmdb.org/t/p/w500" + pop.poster_path}
id="movie-img"
/>
</div>
</Fragment>
))}

how to handle multiple refs inside a map function

I need to target all the components inside map function, but I am only getting the last component inside it.
products?.map((product, i) => (
<div
key={product.id}
className="product__card"
onMouseEnter={() => sliderRef.current.slickNext()}
onMouseLeave={() => sliderRef.current.slickPause()}
>
<StyledSlider {...settings} ref={sliderRef}>
{product.assets.map(({ url, id, filename }) => (
<div className="product__image__container" key={id}>
<img src={url} alt={filename} />
</div>
))}
</StyledSlider>
<div className="product__content__container">
<h3 className="slim__heading">{valueChopper(product.name, 23)}</h3>
<p contentEditable="true" dangerouslySetInnerHTML={{ __html: valueChopper(product.description, 30) }} />
<h6 className="slim__heading">Rs. {product.price.formatted}</h6>
</div>
</div>
)
The simplest way is to use one ref to target multiple elements. See the example below. I've modified your code to make it work.
const sliderRef = useRef([]);
products?.map((product, i) => (
<div
key={product.id}
className="product__card"
onMouseEnter={() => sliderRef.current[i].slickNext()}
onMouseLeave={() => sliderRef.current[i].slickPause()}
>
<StyledSlider {...settings} ref={el => sliderRef.current[i] = el}>
{product.assets.map(({ url, id, filename }) => (
<div className="product__image__container" key={id}>
<img src={url} alt={filename} />
</div>
))}
</StyledSlider>
<div className="product__content__container">
<h3 className="slim__heading">{valueChopper(product.name, 23)}</h3>
<p contentEditable="true" dangerouslySetInnerHTML={{ __html:valueChopper(product.description, 30) }} />
<h6 className="slim__heading">Rs. {product.price.formatted}</h6>
</div>
</div>
)

add a class to an element in a .map function with useState react

<div>
{arrayCard.map((item, index) => {
return (<Card key={index}
onClick={(e) => handleClick(index, e)}
src={item} />)
})}
</div>
This is my Card component.
<button onClick={props.onClick} className="focus:outline-none">
<Image
src={props.src}
alt="Card Pic"
width={80}
height={80}
/>
</button>
This is what I have inside my component. What I want to do is add a class to the clicked Card element. Each one has a different index but I don't know what to do with it.
I have a function handleClick and a useState() to do it.
I hope you can help me with this. Thanks!
you need to change your Card Component:
const [isActive, setIsActive] = useState(false);
const handleClick = (e) => {
setIsActive(value => !value);
props.onClick(e);
}
return (
<button onClick={handleClick} className={`focus:outline-none ${isActive ? 'activeClass' : null}`}>
<Image src={props.src} alt="Card Pic" width={80} height={80} />
</button>
);

Updating the product image based on thumbnail img using react hooks

I've developing e-commerce site similar to amazon and in product detail page,when I click on thumbnail image on the left side it should update the right side image.
The problem is on first loading, the default image is not shown but when I click on the thumbnail its getting updated.Is there any way to show the first image of the thumbnail on first render?
The images are dynamic and is based on sequence order, filecatcode etc.
eg : mycompany.com/GetAppImage/?id=7&&filecatcode=F001&&sequence="1"
useEffect(() => {
setProductImgSrc(
getAllImagesBySeq(productData.prodId, productData.noOfImages, true)
);
}, [productData]);
const [productImgUrl , setProductImgUrl] = useState()
const handleThumbImg = (src) =>{
setProductImgUrl(src)
}
<div className="flex-row w-2/12 product-thumbnails cursor-pointer">
{(productImgSrc.thumbnailImgs || []).map((src) => {
return <img src={src} className="w-full object-contain" onClick={()=>handleThumbImg(src)}/>;
})}
</div>
}
<div className="flex-row w-10/12 product-Main-img ml-6">
<img src={productImgUrl} className="w-full object-contain" />
</div>
Try something like this...
useEffect(() => {
Promise.resolve(setProductImgSrc(
getAllImagesBySeq(productData.prodId, productData.noOfImages, true)
))
.then(() => productImgSrc.thumbnailImgs.length > 0 ? setProductImgUrl(productImgSrc.thumbnailImgs[0]) : console.log("thumbnailImgs is empty"))
}, [productData]);
const [productImgUrl , setProductImgUrl] = useState()
const handleThumbImg = (src) =>{
setProductImgUrl(src)
}
<div className="flex-row w-2/12 product-thumbnails cursor-pointer">
{(productImgSrc.thumbnailImgs || []).map((src) => {
return <img src={src} className="w-full object-contain" onClick={()=>handleThumbImg(src)}/>;
})}
</div>
}
<div className="flex-row w-10/12 product-Main-img ml-6">
<img src={productImgUrl} className="w-full object-contain" />
</div>
I would consider refactoring this code btw.

Problem on React, onClick take the last var and note the choosen one

const CharacterList = () => {
const [change, setChange] = React.useState(false);
const QuickSilver= () => setChange(!change);
const SuperMan= () => setChange(!change);
return (
<div id="character" className="list">
<img id="icons" className="one" src={QuickSilverIc} onClick={QuickSilver} />
{change && (
<img className="card" src={QuickSilverStat} />
)}
<img id="icons" className="two" src={SuperManIc} onClick={SuperMan} />
{change && (
<img className="card" src={SuperManStat} />
)}
So, when i click on the QuickSilver img that bring me the img with src={SuperManStat} and not {QuickSilverStat}
If anyone can help me it will be very nice !
ps: yeah i've cut the react components to show you only what i want ofc ive done averything correctly, like import export etc...
what you need to do is create to different useStates one for quicksilver and the other one for superman, or create an object with this shape
const [hero, SetHero] = useState({superman: false, quicksilver:false});
and then in your onClickhandler you can pass the name and update the state to right hero
{change ? (
<>
<img id="icons" className="one" src={QuickSilverIc} onClick={QuickSilver} />
<img className="card" src={QuickSilverStat} />
</>
) : (
<>
<img id="icons" className="two" src={SuperManIc} onClick={SuperMan} />
<img className="card" src={SuperManStat} />
</>
)}
As I understand, you want to show only one of them. To achieve this, you should wrap both images of each one together, and show only one of them on each change value (true or false):
{change && (
<div>
<img id="icons" className="one" src={QuickSilverIc} onClick={QuickSilver} />
<img className="card" src={QuickSilverStat} />
</div>
)}
{!change && (
<div>
<img id="icons" className="two" src={SuperManIc} onClick={SuperMan} />
<img className="card" src={SuperManStat} />
</div>
)}

Categories

Resources