how do I process clicks on <g>
elements in svg using React
. I know how it could be done if it was 1 element. But how to add such a handler for each element in svg
.
const App = (props) => {
return (
<svg
width={247}
height={160}
viewBox="0 0 247 160"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<g onClick={handleClick}>
<rect width={57} height={62} />
<foreignObject width={57} height={62}>
<div className="item">
<div className="category">Cat1</div>
<div className="label">Label1</div>
</div>
</foreignObject>
</g>
<g>
<rect x={76} width={82} height={62} />
<foreignObject x={76} width={82} height={62}>
<div className="item">
<div className="category">Cat2</div>
<div className="label">Label2</div>
</div>
</foreignObject>
</g>
<g>
<rect x={185} width={62} height={160} />
<foreignObject x={185} width={62} height={160}>
<div className="item">
<div className="category">Cat2</div>
<div className="label">Label2</div>
</div>
</foreignObject>
</g>
<g>
<rect y={80} width={158} height={80} />
<foreignObject y={80} width={158} height={80}>
<div className="item">
<div className="category">Cat2</div>
<div className="label">Label2</div>
</div>
</foreignObject>
</g>
</svg>
)
}
ReactDOM.render(<App/>, document.getElementById("root"))
.item {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background:#D9D9D9
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
CodePudding user response:
There are tons of ways to solve this.
One of those is keeping an array in which you will save the 'active' items, in this case I've used a simple numeric id, but that can be improved with map()
or id={}
and getting the correct ID from the click event.
Then you can change the color if the <g>
's id is present in the array
const { useState } = React;
const App = (props) => {
const [ active, setActive ] = useState([ ]);
const handleClick = (n) => {
let newActives = [ ...active ];
let index = newActives.indexOf(n);
if (index >= 0) {
newActives.splice(index, 1);
} else {
newActives.push(n);
}
setActive(newActives);
}
const getColor = (n) => {
return active.includes(n) ? 'red' : 'lightgrey';
}
return (
<svg
width={247}
height={160}
viewBox="0 0 247 160"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<g onClick={() => handleClick(1)}>
<rect width={57} height={62} />
<foreignObject width={57} height={62}>
<div className="item" style={{ background: getColor(1) }}>
<div className="category">Cat1</div>
<div className="label">Label1</div>
</div>
</foreignObject>
</g>
<g onClick={() => handleClick(2)}>
<rect x={76} width={82} height={62} />
<foreignObject x={76} width={82} height={62}>
<div className="item" style={{ background: getColor(2) }}>
<div className="category">Cat2</div>
<div className="label">Label2</div>
</div>
</foreignObject>
</g>
<g onClick={() => handleClick(3)}>
<rect x={185} width={62} height={160} />
<foreignObject x={185} width={62} height={160}>
<div className="item" style={{ background: getColor(3) }}>
<div className="category">Cat2</div>
<div className="label">Label2</div>
</div>
</foreignObject>
</g>
<g onClick={() => handleClick(4)}>
<rect y={80} width={158} height={80} />
<foreignObject y={80} width={158} height={80}>
<div className="item" style={{ background: getColor(4) }}>
<div className="category">Cat2</div>
<div className="label">Label2</div>
</div>
</foreignObject>
</g>
</svg>
)
}
ReactDOM.render(<App/>, document.getElementById("root"))
.item {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background:#D9D9D9
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
CodePudding user response:
You can do it with style property on svg, store it in useSatate and modify on click.
const [color, setColor] = useState({color: 'black'});
const handleClick = () => {
setColor({color: 'green'})
}
return (
<svg
style={color} ...