Home > Net >  How to change its background when clicking on an svg element
How to change its background when clicking on an svg element

Time:09-16

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} ... 
  • Related