Style hover on mapped array using Reactjs - javascript

I am trying to style the ListItem on hover, but the problem is that the list is being mapped over to create multiple list items. When I change the hover style it changes the style for all list items when being hovered. How do I target just one element? Below is the code.
I am trying to style the prop.icon and ListItemText when they are hovered.
Sidebar.js
var links = (
<List className={classes.list}>
{routes.map((prop, key) => {
if (prop.path === "/login") {
return;
}
return (
<NavLink
to={prop.layout + prop.path}
className={classes.item}
activeClassName="active"
key={key}
>
<ListItem button className={classes.itemLink} onMouseEnter={MouseEnter} onMouseLeave={MouseLeave}>
<prop.icon
className={classNames(classes.itemIcon)}
/>
<ListItemText
primary={prop.name}
className={classNames(classes.itemText)}
disableTypography={true}
/>
</ListItem>
</NavLink>
);
})}
</List>
);
MouseEnter & MouseLeave
const MouseEnter = (e) => {
setHovered(true);
}
const MouseLeave = (e) => {
setHovered(false);
}

With CSS it would be as simple as that:
.list-item:hover{
background-color: purple;
color: white;
}
/* just for decoration, you don't need the code below*/
ul{
list-style-type: none;
}
li{
padding: 1rem;
cursor: pointer;
border: solid 1px black;
margin: 0.5rem;
}
<script src="https://kit.fontawesome.com/a076d05399.js"></script>
<ul >
<li class="list-item" >
<span >♠ </span>spades
</li>
<li class="list-item">
<span >♣ </span>clubs
</li>
<li class="list-item">
<span >♦ </span>dimonds
</li>
</ul>
or with your code:
//links.css
.list-item:hover{
background-color:red;
}
//links.js
import "./links.css"
var links = (
<List className={classes.list}>
{routes.map((prop, key) => {
if (prop.path === "/login") {
return;
}
return (
<NavLink
to={prop.layout + prop.path}
className={classes.item}
activeClassName="active"
key={key}
>
<ListItem button className={classes.itemLink + " list-item"} onMouseEnter={MouseEnter} onMouseLeave={MouseLeave}>
<prop.icon
className={classNames(classes.itemIcon)}
/>
<ListItemText
primary={prop.name}
className={classNames(classes.itemText)}
disableTypography={true}
/>
</ListItem>
</NavLink>
);
})}
</List>
);

Using the active index in the array worked for me besides using boolean values.
sidebar.js
const [hovered, setHovered] = useState(-1);
const MouseEnter = (index) => {
setHovered(index);
}
const MouseLeave = () => {
setHovered(-1);
}
var links = (
<List className={classes.list}>
{routes.map((prop, index, key ) => {
if (prop.path === "/login") {
return;
}
return (
<NavLink
to={prop.layout + prop.path}
className={classes.item}
activeClassName="active"
key={key}
>
<ListItem button className={hovered === index ? 'hoverLinear' : classes.itemContainer} onMouseEnter={() => MouseEnter(index)} onMouseLeave={MouseLeave}>
<prop.icon
className={classNames(classes.itemIcon)}
style={hovered === index ? {color: 'white'} : {color: "#8B2CF5"}}
/>
<ListItemText
primary={prop.name}
className={classNames(classes.itemText, ' mx-10')}
style={hovered === index ? {color: 'white'} : {color: "#3A448E"}}
disableTypography={true}
/>
</ListItem>
</NavLink>
);
})}
</List>
);

Related

NavLink Active property does not work with nested pages

I am using Reactjs in my project. NavLink active property does not work with nested pages inside the same tab?? It works just in the main tab anything inside the main tab does not work with. I want the white border appear in the main tab while open any pages inside it.
import { NavLink } from "react-router-dom";
function Navbar() {
let activeStyle = {
border: "solid 3px #FFF",
};
return (
<>
<nav className="navbar">
<ul className="nav-items">
{navItems.map((item) => {
if (item.id === "3") {
return (
<li
key={item.id}
className={item.cName}
onMouseClick={() => setDropdown(true)}
onMouseEnter={() => setDropdown(true)}
onMouseLeave={() => setDropdown(false)}
>
<NavLink to={item.title}
style={({ isActive }) =>
isActive ? activeStyle: undefined
}
>{item.title}</NavLink>
{dropdown && <Dropdown />}
</li>
);
}
return (
<li key={item.id}
className={item.cName}>
<NavLink to={item.path}
style={({ isActive }) =>
isActive ? activeStyle: undefined
}>{item.title}</NavLink>
</li>
);
})}
</ul>
</nav>
</>
);
}
export default Navbar;

How to style active navigator item in the navbar?

I am working with Reactjs using html, css, and javascript.
I want to style active navigator item in the navbar with white border. That is mean the border will stay in the item while the page of this item is open.
import React, { useState } from "react";
import { Link } from "react-router-dom";
import "./Navbar.css";
import { navItems } from "./NavItems";
import Dropdown from "./Dropdown";
function Navbar() {
const [dropdown, setDropdown] = useState(false);
return (
<>
<nav className="navbar">
<ul className="nav-items">
{navItems.map((item) => {
if (item.id === "C3") {
return (
<li
key={item.id}
className={item.cName}
id={window.location.pathname === item.path? "C10":""}
onMouseClick={() => setDropdown(true)}
onMouseEnter={() => setDropdown(true)}
onMouseLeave={() => setDropdown(false)}
>
<Link to={item.title}>{item.title}</Link>
{dropdown && <Dropdown />}
</li>
);
}
return (
<li key={item.id}
className={item.cName}
id={window.location.pathname === item.path? "C10":""}
>
<Link to={item.path}>{item.title}</Link>
</li>
);
})}
</ul>
</nav>
</>
);
}
export default Navbar;
This is the CSS file
.nav-item a:hover {
border: solid 2px white;
}
#C10{
border: solid 2px white;
}
I try to use this method in CSS file.
.nav-item a:active {
border: solid 2px white;
}
and I use this method in CSS file and JS file also but it does not work!
id={window.location.pathname === item.path? "C10":""}
#C10{
border: solid 2px white;
}
If you want to style an active <Link/> you have to use a <Navlink/> instead.
Check the doc: https://reactrouter.com/en/main/components/nav-link
As William said you are better off using the actual <NavLink/> elements then programming plain html in React. However you can also create a function that adds the active class to the li item when it's selected.
See this example:
const links = ['https://google.com', 'https://google.be', 'https://google.nl'];
const [active, setActive] = useState(null);
<ul>
{links.map((link) => (
<li className="nav-item">
<a
href={`#${link}`}
className={`nav-link ${active == link && 'm-active'}`}
onClick={() => setActive(link)}
>{link}</a>
</li>
))}
</ul>

Removing Duplicates in Axios React JS

This is my code to display all the bond actors and directors, but when displaying, it has multiple of the same names, and I want to remove the duplicates.
Prettier 2.7.1
Playground link
--parser babel
Input:
import React, { useState } from "react";
import styled from "styled-components";
import axios from "axios";
const Button = styled.button`
background-color: black;
color: white;
font-size: 20px;
padding: 10px 60px;
border-radius: 5px;
margin: 10px 0px;
cursor: pointer;
`;
function ButtonClick() {
const [users, setUsers] = useState();
const fetchData = (type) => {
axios.get("https://iznfqs92n3.execute-api.us-west-1.amazonaws.com/dev/api/v2/movies")
.then((response) => {
setUsers({ [type]: Array.from(new Set(response.data))});
});
};
return (
<div>
<div>
<h2>Bond Database</h2>
<h5>Click on the buttons to see the list of all the bond movie's directors, bond actors, release year, and title songs</h5>
<Button onClick={() => fetchData("bond_actor")}>Bond Actors</Button>
{users && (
<ul>
{users?.bond_actor?.map((user) => (
<li key={user.id}>{user.bond_actor}</li>
))}
</ul>
)}
</div>
<div>
<Button onClick={() => fetchData("directors")}>Directors</Button>
{users && (
<ul>
{users?.directors?.map((user) => (
<li key={user.id}>{user.director}</li>
))}
</ul>
)}
</div>
<div>
<Button onClick={() => fetchData("title_song")}>Songs</Button>
{users && (
<ul>
{users?.title_song?.map((user) => (
<li key={user.id}>{user.title_song}</li>
))}
</ul>
)}
</div>
<div>
<Button onClick={() => fetchData("movie_year")}>Movie Year</Button>
{users && (
<ul>
{users?.movie_year?.map((user) => (
<li key={user.id}>{user.movie_year}</li>
))}
</ul>
)}
</div>
</div>
);
}
export default ButtonClick;
I tried an Array, and a Set in fetchData, but I'm having trouble implementing it. I'm just not sure if I'm putting the proper code in the right place. I would appreciate it if I get some assistance on this. Thank you!
you need to map you required data and filter out unique values from objects. I have update code it should work for you.
import React, { useState } from "react";
import styled from "styled-components";
import axios from "axios";
const Button = styled.button`
background-color: black;
color: white;
font-size: 20px;
padding: 10px 60px;
border-radius: 5px;
margin: 10px 0px;
cursor: pointer;
`;
function ButtonClick() {
const [users, setUsers] = useState();
const fetchData = (type) => {
axios
.get(
"https://iznfqs92n3.execute-api.us-west-1.amazonaws.com/dev/api/v2/movies"
)
.then((response) => {
const mappedData = (response.data || [])
.map((item) => item[type] || (type === "directors" && item.director))
.filter((newItem) => newItem);
setUsers({ [type]: Array.from([...new Set(mappedData)]) });
});
};
return (
<div>
<div>
<h2>Bond Database</h2>
<h5>
Click on the buttons to see the list of all the bond movie's
directors, bond actors, release year, and title songs
</h5>
<Button onClick={() => fetchData("bond_actor")}>Bond Actors</Button>
{users && (
<ul>
{users?.bond_actor?.map((user, index) => (
<li key={index}>{user}</li>
))}
</ul>
)}
</div>
<div>
<Button onClick={() => fetchData("directors")}>Directors</Button>
{users && (
<ul>
{users?.directors?.map((director) => (
<li key={director}>{director}</li>
))}
</ul>
)}
</div>
<div>
<Button onClick={() => fetchData("title_song")}>Songs</Button>
{users && (
<ul>
{users?.title_song?.map((title_song) => (
<li key={title_song}>{title_song}</li>
))}
</ul>
)}
</div>
<div>
<Button onClick={() => fetchData("movie_year")}>Movie Year</Button>
{users && (
<ul>
{users?.movie_year?.map((movie_year) => (
<li key={movie_year}>{movie_year}</li>
))}
</ul>
)}
</div>
</div>
);
}
export default ButtonClick;

onClick in Map func,only execute for one element

Is there any method for map function to only execute Dropdown menu for one cart.For now if i click on one Menu icon , every dropdown appears for every Card in map.
const [isOpen, setIsOpen] = useState(false);
{albums.map((album, i) => (
<Card width="23rem" height="16rem" bckImg={album.bckImgUrl} key={i}>
<AlbumtTitle color={album.color}>{album.name}</AlbumtTitle>
<LinkButton background={album.color} to={`/albums/${album._id}`}>
See more
</LinkButton>
<KebabMenu onClick={() => setIsOpen(!isOpen)} open={isOpen}>
{isOpen ? (
<Dropdown
ref={ref}
style={{
width: "50px",
height: "50px",
}}
>
<li>
<Link to={`editAlbum/${album._id}`}>Edit</Link>
</li>
<hr />
<li>Delete</li>
</Dropdown>
) : (
<Dots />
)}
</KebabMenu>
</Card>
))}
Your isOpen state can be an object that tracks the open state per item in the album:
const [isOpen, setIsOpen] = useState({});
{albums.map((album, i) => (
<Card width="23rem" height="16rem" bckImg={album.bckImgUrl} key={i}>
<AlbumtTitle color={album.color}>{album.name}</AlbumtTitle>
<LinkButton background={album.color} to={`/albums/${album._id}`}>
See more
</LinkButton>
<KebabMenu
onClick={() =>
setIsOpen({ ...isOpen, [album._id]: !isOpen[album._id] })
}
open={!!isOpen[album._id]}
>
{isOpen[album._id] ? (
<Dropdown
ref={ref}
style={{
width: "50px",
height: "50px",
}}
>
<li>
<Link to={`editAlbum/${album._id}`}>Edit</Link>
</li>
<hr />
<li>Delete</li>
</Dropdown>
) : (
<Dots />
)}
</KebabMenu>
</Card>
))}
Instead of
{albums.map((album, i) => (...your code...))}
you need to check whether you have at least one item in albums, and if yes, process it:
{if (albums.length > 0) {
return albums.slice(0,1).map((album, i) => (...your code...))
} else {
return null // or any other actions you want to take in case there are no albums
}
}
To clarify, we check the length of the albums, if it is not empty. If it is, we take only the first element and process it.

Reactjs dropdown menu not displaying when hovered over the word

can someone tells me why the dropdown menu is not displaying in this demo? the dropdown menu should show when I hover over the word 'collective'?
https://codesandbox.io/s/funny-river-c76hu
For the app to work, you would have to type in the input box "collective", click analyse, then a progressbar will show, click on the blue line in the progressbar, an underline would show under the word "collective" then you should hover over "collective" word and a drop down menu should be displayed but the whole window disappears when I hover over the word "collective" instead of the drop down menu
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import { Content, Dropdown, Label, Progress, Button, Box } from "rbx";
import "rbx/index.css";
function App() {
const [serverResponse, setServerResponse] = useState(null);
const [text, setText] = useState([]);
const [loading, setLoading] = useState(false);
const [modifiedText, setModifiedText] = useState(null);
const [selectedSentiment, setSentiment] = useState(null);
const [dropdownContent, setDropdownContent] = useState([]);
const [isCorrected, setIsCorrected] = useState(false);
const [displayDrop, setDisplayDrop] = useState(false);
useEffect(() => {
if (serverResponse && selectedSentiment) {
const newText = Object.entries(serverResponse[selectedSentiment]).map(
([word, recommendations]) => {
const parts = text[0].split(word);
const newText = [];
parts.forEach((part, index) => {
newText.push(part);
if (index !== parts.length - 1) {
newText.push(
<u
className="dropbtn"
data-replaces={word}
onMouseOver={() => {
setDropdownContent(recommendations);
setDisplayDrop(true);
}}
>
{word}
</u>
);
}
});
return newText;
}
);
setModifiedText(newText.flat());
}
}, [serverResponse, text, selectedSentiment]);
const handleAnalysis = () => {
setLoading(true);
setTimeout(() => {
setLoading(false);
setServerResponse({ joy: { collective: ["inner", "constant"] } });
}, 1500);
};
const handleTextChange = event => {
setText([event.target.innerText]);
};
const replaceText = wordToReplaceWith => {
const replacedWord = Object.entries(serverResponse[selectedSentiment]).find(
([word, recommendations]) => recommendations.includes(wordToReplaceWith)
)[0];
setText([
text[0].replace(new RegExp(replacedWord, "g"), wordToReplaceWith)
]);
setModifiedText(null);
setServerResponse(null);
setIsCorrected(true);
setDropdownContent([]);
};
const hasResponse = serverResponse !== null;
return (
<Box>
<Content>
<div
onInput={handleTextChange}
contentEditable={!hasResponse}
style={{ border: "1px solid red" }}
>
{hasResponse && modifiedText
? modifiedText.map((text, index) => <span key={index}>{text}</span>)
: isCorrected
? text[0]
: ""}
</div>
<br />
{displayDrop ? (
<div
id="myDropdown"
class="dropdown-content"
onClick={() => setDisplayDrop(false)}
>
dropdownContent.map((content, index) => (
<>
<strong onClick={() => replaceText(content)} key={index}>
{content}
</strong>{" "}
</>
))
</div>
) : null}
<br />
<Button
color="primary"
onClick={handleAnalysis}
disabled={loading || text.length === 0}
>
analyze
</Button>
<hr />
{hasResponse && (
<Label>
Joy{" "}
<Progress
value={Math.random() * 100}
color="info"
onClick={() => setSentiment("joy")}
/>
</Label>
)}
</Content>
</Box>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
css file
.App {
font-family: sans-serif;
text-align: center;
}
.highlight {
background: red;
text-decoration: underline;
}
.dropbtn {
color: white;
font-size: 16px;
border: none;
cursor: pointer;
}
.dropbtn:hover,
.dropbtn:focus {
background-color: #2980b9;
}
.dropdown {
position: relative;
display: inline-block;
}
.dropdown-content {
position: relative;
background-color: #f1f1f1;
min-width: 160px;
overflow: auto;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 1;
}
.show {
display: block;
}
The problem is this:
{displayDrop ? (
<div
id="myDropdown"
class="dropdown-content"
onClick={() => setDisplayDrop(false)}
>
dropdownContent.map((content, index) => (
<>
<strong onClick={() => replaceText(content)} key={index}>
{content}
</strong>{" "}
</>
))
</div>
) : null}
You are missing a pair of curly brackets around dropdownContent. It should be:
{displayDrop ? (
<div
id="myDropdown"
class="dropdown-content"
onClick={() => setDisplayDrop(false)}
>
{dropdownContent.map((content, index) => (
<>
<strong onClick={() => replaceText(content)} key={index}>
{content}
</strong>{" "}
</>
))}
</div>
) : null}
A working sandbox here https://codesandbox.io/embed/fast-feather-lvpk7 which is now displaying this content.

Categories

Resources