'How to overlay text on an SVG path element in React without using the coordinates of the path element?

I am attempting to create a global COVID-19 tracker for each country using React. In order to carve out individual countries and present them I have used an SVG element as shown:

  <svg
    xmlns="http://www.w3.org/2000/svg"
    width="inherit"
    height="inherit"
    fill="#ececec"
    stroke="#000"
    strokeLinecap="round"
    strokeLinejoin="round"
    strokeWidth="0.2"
    version="1.2"
    viewBox="0 0 2000 857"
    className="world-map">
    <!-- More Svg -->
    <path
      d="M669.1 851.7l-3-.2h-5l-6-13.6 3.1 2.8 4.3 4.6 7.8 3.7 7.3 1.5-.8 3-4.4.3-3.3-2.1zM638.6 644.7l11.3 10.4 4.6 1 7.3 4.8 5.9 2.5 1.1 2.8-4.2 9.8 5.8 1.7 6.3 1 4.2-1 4.3-5 .3-5.6 2.6-1.3 3.2 3.8.4 5.1-4.2 3.5-3.3 2.6-5.3 6.3-6 8.7-.5 5.2-.4 6.6 1.2 6.4-.9 1.4.4 4.1.3 3.4 7.8 5.5.2 4.4 3.9 2.8.3 3.1-3.3 8.2-7 3.5-10.2 1.3-6-.7 2.1 3.9.1 4.7 1.8 3.2-2.5 2.3-5.1.9-5.6-2.4-1.5 1.7 2.5 6.3 4 1.9 2.3-2 2.5 3.3-4.2 2-2.9 4 1.2 6.3-.1 3.4h-4.8l-3 3.2.1 4.8 6.5 4.6 5.2 1.2.2 5.7-4.6 3.5-.6 7.3-3.5 2.4-.9 2.9 4.2 6.5 4.6 3.5-2.1-.3-4.9-1-12.1-.8-3.5-3.6-1.9-4.6-3.1.4-2.6-2.3-3.1-6.5 2.7-2.8.1-3.9-1.8-3.2.7-5.4-1.1-8.3-1.8-3.7 1.8-1.2-1.4-2.4-2.8-1.2.8-2.7-3.1-2.4-3.7-7.3 1.7-1.3-3.3-7.8-.8-6.5-.2-5.7 2.5-2.3-3.3-6.3-1.6-5.8 3-4.2-1.4-5.4 1.6-6.2-1.4-5.9-1.6-1.2-4.9-11.1 2.1-6.6-1.7-6.2.9-5.9 2.6-6 3.3-4-2-2.5.8-2.1-1.6-10.7 5.6-3.1 1.2-6.7-.9-1.6 4-5.8 7.5 1.6 3.7 4.6 1.6-5.2 6.4.3 1 1.4z"
      className="Argentina"
      fill={colorMap[covidMap["Argentina"]]}></path>
    <path
      d="M645.5 212.5l-2.2-3.6 2.9-8.5-1.6-1.8-3.7 1-1.1-1.6-5.5 4.7-3.2 4.9-2.8 2.9-2.5 1-1.7.3-1.1 1.5h-9.3l-7.8.1-2.7 1.1-6.8 4.4v-.1l-.9-.4-2 .9-1.9 1.3-1.8-1.1-4.7.8-3.9.9-1.9.8-2.3 2.1 1.8.7 1.7-.4h.3l-.3 1.9-4.8.7-2.8.8-1.7 1-2.6-.6-1.6.3-2.9 1.8-4.6 2-2.7-.4 2-2.2 3.7-3.5 4.1-2.1 1.1-1.8.9-3 3.8-3.5.9-4 1.1 3.9 3.8.9 2.4-2.1-1.4-4.8-.9-2-4-1.2-3.8-.7h-3.9l-3.4-.8-.4-1.4-1.4.9-1.2-.2 1.9-2.1-1.8-.8 1.9-2.4-1.2-1.8 1.7-1.8-5.2-.9-.1-3.6-.8-.8-3.3-.2-4.1-1.2-1.5.8-1.8 1.5-3.3 1-3.1 2.5-5.4-1.7-4.4.8-3.9-1.9-4.6-1-3.3-.4-1-1 .9-3.4h-1.7l-1.3 2.4H377l-5.4-6.1-1.6-2.7-7-2.6 1.3-5.5 3.6-3.7-4.1-2.7 3.1-4.9-2.1-4.4 2.5-3.2 5.1-2.9 3.2-3.8-4.6-3.8 1.4-6.9 1.1-4.2-1.6-2.7-.8-2.4.6-3.1-6.5 1.9-7.6 3.3-.3-3.8-.5-2.6-2.8-1.6-4.2-.2L385.4 87 410 66.6l6 1.3 3.3 2.6 3.7.5 6.3-2.2 7-1.7 5.3.6 8.9-2.3 8.2-1.3.2 2.2 4.5-1.3 3.9-2.5 2.1.6 1.4 4.8 9.5-3.7-3.9 4.1 6-.9 3.2-1.5 4.6.3 3.9 2.2 7.5 2 4.7.9 4.4-.3 2.9 2.8-8.5 2.7 6.4 1.1 11.9-.6 4.4-1 1.4 3.3 7.1-2.7-2.1-2.4 4.5-1.8 5.2-.3 3.9-.5 2.1 1.3 1.5 2.9 5-.4 5.3 2.5 7.2-.9 6 .1 2.4-3.4 4.5-.9 4.9 1.8-4.3 5.2 6.2-4.4 3.2.2 6.4-5.5-1.6-3.3-2.9-2.2 5.5-5.9 8.2-3.8 4.5.9 2 2.3.4 6-5.8 2.6 6.7 1.1-4.4 5.5 8.9-4.2 2.2 3.5-4.3 4 1.3 3.7 7.3-3.9 6.5-4.8 4.7-5.9 5.5.4 5.4.8 3.6 2.7-1.7 2.7-5.1 2.9.9 2.9-2.4 2.7-10.9 3.9-6.5.9-3.2-1.7-3.3 2.8-7.4 4.7-3 2.5-7.7 3.8-6.5.4-5.1 2.4-2.9 3.8-5.7.7-8.7 4.7-9.4 6.5-5 4.6-4.9 6.9 6 1-1.5 5.5-.8 4.6 7.3-1.2 7 2.6 3.3 2.3 1.7 2.8 4.9 1.7 3.6 2.5 7.6.4 4.8.6-3.6 5.2-1.7 6.1.1 6.9 4.4 5.9 4.7-2 5.6-6.4 2.3-9.6-1.7-3.2 9-2.9 7.5-4.2 4.8-4.2 1.7-4-.4-5.1-3.2-4.5 8.9-6.2 1-5.3 3.9-9 3.8-1.4 6.7 1.6 4.2.6 4.5-1.6 3.1 2 3.6 3.4.2 2.2 7.7.5-2.6 4.9-2.3 7.4 3.8 1 1.6 3.5 8.2-3.3 7.5-6.6 4.2-2.7 1.1 5.3 2.6 7.5 2 7.2-3.4 3.8 4.8 3.4 2.9 3.4 6.9 1.6 2.4 1.9v5.2l3.4.8 1.1 2.3-2 6.9-4.3 2.3-4.2 2.2-8.8 2.2-7.9 5-8.6 1.1-10.1-1.4h-7.3l-5.3.4-5.7 4.5-7.4 2.8-10.1 8.2-7.9 5.8 4.7-1 10.9-8.3 12.3-5.2 7.6-.6 3.3 3.1-6.1 4.2-.6 6.7.1 4.8 5.6 3.1 8.6-.9 7.2-7.1-1 4.6 2.5 2.3-7.4 4.1-12.4 3.8-5.8 2.5-7.2 4.6-3.7-.5 1.5-5.3 10.4-5.3-8.1.2-5.9.8z"
      className="Canada"
      fill={/*colorMap[covidMap["Canada"]]*/"orange"}
      id="canada"></path>
    <text style={{fontSize:"30px"}} textAnchor="middle"><textPath href="#canada">Text</textPath></text>
    <!-- More Svg -->
  </svg>

enter image description here

Now I want to achieve 2 things:

  1. How can I add a text overlay that is directly in the center of the path elements, i.e. in the example above I had unsuccessfully attempted to use SVG text & textPath elements to label the path filled in orange with the text: 'Canada'?
  2. How can I add a hover effect, I have tried adding path:hover, svg path:hover and even .Canada:hover to my CSS file with the attributes; backgroundColor:'blue' + fill:'blue'. Neither of these attempts have worked. Any idea what I could possibly do in order to achieve the above?

I have looked at several stack overflow links and none have resolved my issue. Ideally I want to avoid performing any manual javascript functions that involve the paths coordinates.



Solution 1:[1]

1. You can add the text with this way on SVG file
<svg>
...
<text transform="matrix(1 0 0 1 418.5 143.5996)"  fontSize="36">Canada</text>
</svg>

enter image description here

You can change the position and styles.

2. To apply hover effect

There's no fill so the interior does not catch mouse events by default and therefore hover doesn't react to that. Changing pointer-events to all will fix it in this case:

path{
    fill:none;
    stroke:black;
    pointer-events:all;
}

You can also refer this link. https://jsfiddle.net/zuul/k7a3q5xm/

Solution 2:[2]

  • <textPath> align won't work because the path is the Country outline
  • you need path.getBBox() to calculate the center of the Country
  • the center is the X,Y location for a <text> Country name
  • I have added a <g> group tag around each Country <path> for convience
  • so the <text> element is injected immediately after the Country <path> element
  • to make the CSS adjacent selector work: path:hover+text { font-size: 50px; fill: white }
  • purposely no React code (you can do that yourself), 100% clean SVG answer
  • per Robert his comment, added a <title> tag

<style>svg{height:180px}</style>
<svg id=WORLD xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 240">
    <style>
      *               { pointer-events: none }
      path            { pointer-events: all; stroke: grey }
      text            { text-anchor: middle }
      path:hover      { stroke: black; fill: blue }
      path:hover+text { font-size: 50px; fill: white }
    </style>
    <rect width="100%" height="100%" fill="lightblue"></rect>
    <g>
      <path id="Chile" fill="green" d="m480 222-3 0h-5l-6-14 3 3 4 5 8 4 7 2-1 3-4 0-3-2zm-30-207 11 10 5 1 7 5 6 3 1 3-4 10 6 2 6 1 4-1 4-5 0-6 3-1 3 4 0 5-4 4-3 3-5 6-6 9-1 5 0 7 1 6-1 1 0 4 0 3 8 6 0 4 4 3 0 3-3 8-7 4-10 1-6-1 2 4 0 5 2 3-3 2-5 1-6-2-2 2 3 6 4 2 2-2 3 3-4 2-3 4 1 6 0 3h-5l-3 3 0 5 7 5 5 1 0 6-5 4-1 7-4 2-1 3 4 7 5 4-2 0-5-1-12-1-4-4-2-5-3 0-3-2-3-7 3-3 0-4-2-3 1-5-1-8-2-4 2-1-1-2-3-1 1-3-3-2-4-7 2-1-3-8-1-7 0-6 3-2-3-6-2-6 3-4-1-5 2-6-1-6-2-1-5-11 2-7-2-6 1-6 3-6 3-4-2-3 1-2-2-11 6-3 1-7-1-2 4-6 8 2 4 5 2-5 6 0 1 1z"></path>
    </g>
    <g>
      <path id="Canada" fill="orange" pathLength="100" d="m310 180-2-4 3-9-2-2-4 1-1-2-6 5-3 5-3 3-3 1-2 0-1 2h-9l-8 0-3 1-7 4v0l-1 0-2 1-2 1-2-1-5 1-4 1-2 1-2 2 2 1 2 0h0l0 2-5 1-3 1-2 1-3-1-2 0-3 2-5 2-3 0 2-2 4-4 4-2 1-2 1-3 4-4 1-4 1 4 4 1 2-2-1-5-1-2-4-1-4-1h-4l-3-1 0-1-1 1-1 0 2-2-2-1 2-2-1-2 2-2-5-1 0-4-1-1-3 0-4-1-2 1-2 2-3 1-3 3-5-2-4 1-4-2-5-1-3 0-1-1 1-3h-2l-1 2h-137l-5-6-2-3-7-3 1-6 4-4-4-3 3-5-2-4 3-3 5-3 3-4-5-4 1-7 1-4-2-3-1-2 1-3-7 2-8 3 0-4-1-3-3-2-4 0 37-32 25-20 6 1 3 3 4 1 6-2 7-2 5 1 9-2 8-1 0 2 5-1 4-3 2 1 1 5 10-4-4 4 6-1 3-2 5 0 4 2 8 2 5 1 4 0 3 3-9 3 6 1 12-1 4-1 1 3 7-3-2-2 5-2 5 0 4-1 2 1 2 3 5 0 5 3 7-1 6 0 2-3 5-1 5 2-4 5 6-4 3 0 6-6-2-3-3-2 6-6 8-4 5 1 2 2 0 6-6 3 7 1-4 6 9-4 2 4-4 4 1 4 7-4 7-5 5-6 6 0 5 1 4 3-2 3-5 3 1 3-2 3-11 4-7 1-3-2-3 3-7 5-3 3-8 4-7 0-5 2-3 4-6 1-9 5-9 7-5 5-5 7 6 1-2 6-1 5 7-1 7 3 3 2 2 3 5 2 4 3 8 0 5 1-4 5-2 6 0 7 4 6 5-2 6-6 2-10-2-3 9-3 8-4 5-4 2-4 0-5-3-5 9-6 1-5 4-9 4-1 7 2 4 1 5-2 3 2 4 3 0 2 8 1-3 5-2 7 4 1 2 4 8-3 8-7 4-3 1 5 3 8 2 7-3 4 5 3 3 3 7 2 2 2v5l3 1 1 2-2 7-4 2-4 2-9 2-8 5-9 1-10-1h-7l-5 0-6 5-7 3-10 8-8 6 5-1 11-8 12-5 8-1 3 3-6 4-1 7 0 5 6 3 9-1 7-7-1 5 3 2-7 4-12 4-6 3-7 5-4-1 2-5 10-5-8 0-6 1z"></path>
    </g>
    <text font-size="60px"><textPath startoffset="36" href="#Canada">Canada</textPath></text>
  </svg>
  <script>
    WORLD.querySelectorAll("path").forEach(country => {
      let { x, y, width, height } = country.getBBox();
      let cx = x + width / 2;
      let cy = y + height / 2;
      let center = document.createElementNS("http://www.w3.org/2000/svg", "circle");
      center.setAttribute("cx", cx);
      center.setAttribute("cy", cy);
      center.setAttribute("r", 5);
      center.setAttribute("fill", "red");
      let name = document.createElementNS("http://www.w3.org/2000/svg", "text");
      name.setAttribute("x", cx);
      name.setAttribute("y", cy - 5);
      name.innerHTML = country.id;
      let title = document.createElementNS("http://www.w3.org/2000/svg", "title");
      title.innerHTML = country.id;
      country.parentNode.append(name, center, title);
    })
  </script>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Nicholi Jin
Solution 2