javascriptsvg

JavaScript adding event listeners to path elements of svg not working here


const countries = Array.from(document.getElementsByClassName('country'));
countries.forEach(c => {
  c.addEventListener('click', function() {
    console.log('you clicked:', c.getAttribute('title'));
  })
})
.map {
  background: #88aaaa;
}
.map {
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;
  -webkit-align-items: center;
  -ms-flex-align: center;
  align-items: center;

  position: relative;
  visibility: visible;

  width: 100%;
  height: 100%;
  padding: 3rem 1rem;
  opacity: 1;
  transition: opacity 0.5s;

  z-index: -1;
  margin-top: -90px;
}
.map_svg {
  display: block;
  width: 100%;
  height: auto;
  max-height: 100%;
  margin-left: 1rem;
}

.map_svg path {
  fill-opacity: 1;
  stroke: #21252b;
  stroke-opacity: 1;
  stroke-width: 0.5;
  transition: 0.5s;
}
<section class="map">
    <svg class="map_svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
      version="1.1" viewBox="0 0 1008 651">
      <path class="country" id="AT" title="Austria"
        d="M522.861,309.853L522.648,311.557L521.069,311.565L521.613,312.464L520.682,315.111L520.147,315.8L517.695,315.901L516.28,316.823L513.964,316.509L509.955,315.458L509.329,314.034L506.558,314.746L506.231,315.523L504.533,314.943L503.102,314.832L501.833,314.085L502.262,313.078L502.154,312.344L503,312.116L504.419,313.264L504.818,312.173L507.291,312.35L509.295,311.606L510.64,311.733L511.514,312.582L511.775,311.878L511.378,309.158L512.385,308.624L513.374,306.673L515.457,308.037L517.034,306.302L518.021,305.983L520.198,307.281L521.515,307.061L522.807,307.861L522.582,308.396z" />
      <path class="country" id="AU" title="Australia"
        d="M882.928,588.16l2.709,1.277l1.526,-0.508l2.188,-0.71l1.682,0.248l0.199,4.425l-0.961,1.3l-0.289,3.064l-0.98,-1.047l-1.946,2.675l-0.58,-0.208l-1.725,-0.12l-1.729,-3.276l-0.384,-2.496l-1.617,-3.254l0.071,-1.695L882.928,588.16zM877.779,502.097l1.01,2.254l1.799,-1.084l0.929,1.218l1.346,1.125l-0.288,1.28l0.598,2.484l0.426,1.452l0.706,0.355l0.761,2.495l-0.271,1.52l0.908,1.995l3.038,1.542l1.98,1.407l1.881,1.292l-0.367,0.721l1.604,1.872l1.09,3.249l1.119,-0.662l1.137,1.306l0.686,-0.464l0.483,3.208l1.989,1.871l1.302,1.167l2.191,2.488l0.788,2.487l0.072,1.774l-0.193,1.937l1.336,2.676l-0.16,2.811l-0.485,1.48l-0.757,2.871l0.057,1.859l-0.555,2.34l-1.238,2.996l-2.077,1.631l-1.023,2.59l-0.936,1.666l-0.831,2.932l-1.082,1.707l-0.709,2.583l-0.362,2.401l0.144,1.109l-1.607,1.224l-3.139,0.128l-2.588,1.454l-1.288,1.38l-1.694,1.539l-2.322,-1.584l-1.718,-0.629l0.436,-1.851l-1.533,0.67l-2.455,2.582l-2.424,-0.97l-1.59,-0.564l-1.604,-0.254l-2.714,-1.027l-1.813,-2.175l-0.521,-2.655l-0.651,-1.752l-1.378,-1.398l-2.697,-0.414l0.922,-1.661l-0.679,-2.522l-1.369,2.351l-2.495,0.627l1.467,-1.885l0.425,-1.953l1.083,-1.646l-0.225,-2.472l-2.28,2.849l-1.752,1.15l-1.074,2.693l-2.189,-1.396l0.087,-1.791l-1.754,-2.43l-1.479,-1.247l0.527,-0.766l-3.598,-2.001l-1.971,-0.094l-2.696,-1.597l-5.021,0.31l-3.631,1.175l-3.19,1.1l-2.676,-0.219l-2.972,1.696l-2.432,0.766l-0.54,1.75l-1.035,1.363l-2.38,0.082l-1.761,0.299l-2.478,-0.613l-2.017,0.367l-1.925,0.154l-1.668,1.801l-0.817,-0.153l-1.406,0.959l-1.348,1.082l-2.046,-0.134l-1.879,-0.001l-2.975,-2.168l-1.507,-0.642l0.061,-1.927l1.393,-0.456l0.476,-0.761l-0.1,-1.196l0.343,-2.302l-0.313,-1.948l-1.482,-3.294l-0.46,-1.845l0.121,-1.83l-1.116,-2.079l-0.071,-0.934l-1.242,-1.262l-0.35,-2.468l-1.603,-2.477l-0.388,-1.327l1.231,1.346l-0.946,-2.881l1.391,0.898l0.83,1.203l-0.047,-1.59l-1.388,-2.43l-0.269,-0.968l-0.65,-0.917l0.305,-1.767l0.574,-0.75l0.383,-1.519l-0.3,-1.768l1.159,-2.165l0.211,2.292l1.185,-2.071l2.278,-1.002l1.366,-1.276l2.143,-1.095l1.274,-0.232l0.772,0.367l2.209,-1.109l1.701,-0.33l0.425,-0.65l0.742,-0.271l1.55,0.07l2.947,-0.867l1.524,-1.313l0.716,-1.575l1.645,-1.491l0.126,-1.169l0.073,-1.589l1.962,-2.474l1.181,2.514l1.193,-0.582l-0.998,-1.375l0.88,-1.409l1.237,0.629l0.34,-2.205l1.532,-1.421l0.676,-1.138l1.41,-0.491l0.044,-0.804l1.232,0.335l0.049,-0.722l1.233,-0.412l1.355,-0.387l2.071,1.318l1.556,1.705l1.755,0.02l1.783,0.271l-0.594,-1.582l1.343,-2.303l1.264,-0.749l-0.437,-0.715l1.218,-1.632l1.698,-1.006l1.435,0.339l2.355,-0.537l-0.051,-1.455l-2.054,-0.936l1.493,-0.413l1.857,0.704l1.489,1.167l2.361,0.729l0.801,-0.288l1.738,0.875l1.638,-0.815l1.054,0.248l0.656,-0.547l1.287,1.41l-0.747,1.528l-1.064,1.155l-0.964,0.096l0.325,1.146l-0.824,1.435l-0.996,1.414l0.201,0.814l2.229,1.596l2.16,0.928l1.443,0.999l2.027,1.722l0.79,-0.003l1.468,0.746l0.426,0.901l2.677,0.992l1.852,-0.999l0.549,-1.566l0.568,-1.289l0.349,-1.59l0.853,-2.3l-0.39,-1.394l0.202,-0.837l-0.324,-1.643l0.367,-2.157l0.538,-0.581l-0.437,-0.953l0.678,-1.511l0.532,-1.563l0.07,-0.81l1.042,-1.063l0.791,1.388l0.194,1.783l0.699,0.344l0.119,1.197l1.02,1.452l0.21,1.62L877.779,502.097z" />
    </svg>
  </section>
<p class="country" title="p1">one</p>
<p class="country" title="p2">two</p>
<p class="country" title="p3">three</p>

I have a page with an SVG containing multiple paths (this is for a world map), and wanted to make a script to add an event listener to each path, so you can click on an individual country and make things happen, though I am having trouble and not sure why.

I created a very simple script:

const countries = Array.from(document.getElementsByClassName('country'));
countries.forEach(c => {
  console.log(c);
  c.addEventListener('click', function() {
    console.log('you clicked:', c.getAttribute('title'));
  })
})

The script does loop through the elements array and each element is output to the console, mousing over the element in the console highlights the area on the page... but it appears no event listener is created, or bound to the path.

I did check the script is working by creating a set of paragraphs on the page:

<p class="country" title="p1">one</p>
<p class="country" title="p2">two</p>
<p class="country" title="p3">three</p>

These are now picked up by the same script and have event listeners that log the expected results on click... but nothing happens when the paths are clicked on. It seems like chrome just doesn't want to add event listeners to path elements, but I know it should be possible.

for reference the svg is inline defined like this:

    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 1008 651">
      <path class="country" id="AE" title="United Arab Emirates"
        d="M619.874,393.722L620.373,393.573L620.477,394.411L622.671,393.93L624.99,394.009L626.684,394.1L628.604,392.028L630.695,390.054L632.467,388.146L633.001,389.202L633.382,391.639L631.949,391.651L631.72,393.648L632.216,394.073L630.947,394.674L630.939,395.919L630.122,397.175L630.049,398.394L629.484,399.032L621.056,397.508L619.981,394.428z" />
      <path class="country" id="AF" title="Afghanistan"
        d="M646.879,356.901L649.738,358.201L651.853,357.745L652.438,356.188L654.651,355.669L656.231,354.617L656.791,351.832L659.154,351.154L659.594,349.902L660.917,350.843L661.762,350.952L663.323,350.975L665.438,351.716L666.295,352.143L668.324,351.017L669.27,351.694L670.174,350.085L671.85,350.159L672.281,349.641L672.578,348.213L673.785,346.975L675.303,347.785L674.998,348.869L675.846,349.038L675.585,351.994L676.694,353.137L677.672,352.404L678.916,352.057L680.663,350.486L682.594,350.745L685.486,350.751L685.985,351.758L684.353,352.15L682.928,352.795L679.71,353.2L676.699,353.931L675.063,355.439L675.725,356.899L676.046,358.603L674.649,360.033L674.766,361.335L673.995,362.549L671.328,362.444L672.43,364.663L670.646,365.507L669.455,367.511L669.609,369.491L668.514,370.415L667.477,370.109L665.334,370.537L665.027,371.451L662.939,371.446L661.377,373.289L661.278,376.039L657.635,377.374L655.682,377.092L655.114,377.794L653.438,377.386L650.634,377.865L645.936,376.228L648.479,373.298L648.249,371.202L646.125,370.65L645.905,368.565L644.987,365.921L646.187,364.094L644.966,363.599L645.736,361.148z" />
      <path class="country" id="AL" title="Albania"
        d="M532.985,334.657L532.629,335.93L533.027,337.524L534.19,338.425L534.134,339.393L533.223,339.925L533.054,341.115L531.75,342.88L531.274,342.626L531.218,341.826L529.665,340.601L529.421,338.851L529.658,336.323L530.041,335.164L529.568,334.573L529.38,333.377L530.596,331.512L530.774,332.227L531.528,331.891L532.125,332.907L532.796,333.293z" />
//  etc..

I have also tried hardcoding an event listener to a specific path id but that also does not do anything on click, i.e:

const oz = document.getElementById("AU");
console.log(oz);
oz.addEventListener("click", () => {
  console.log('clicked on oz');
});

While trying to find a solution I have seen examples of the same approach working, e.g. https://svg-tutorial.com/svg/interaction

And I can't realistically see where I am doing anything different that would cause a problem

As per request have tried adding a snippet: Edit: Shrunk & updated snippet to avoid issues:


Solution

  • Incorrect Stacking Context

    The problem is caused by applying css z-index: -1 to the wrapper element. This reorders the stacking context so that the document body masks pointer events on the svg paths within the wrapper, i.e., the svg appears behind the body.

    The stacking order can be corrected by removing either z-index: -1 (defaults to "auto") or position: relative (defaults to "static").

    Run the snippet and click on the rectangle path. Then check z-index option and repeat.

    addEventListener('click', (e) => {
      console.log(e.type, e.target.tagName, e.target.getAttribute('title'));
    })
    
    document.querySelector('input').addEventListener('change', (e) => {
      document.querySelector('section').classList.toggle('map');
    })
    .map {
      position: relative;
      z-index: -1;
    }
    <input type="checkbox"> Use Z-Index -1
    
    <section>
      <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
          <path class="country" id="AT" title="Austria" d="M0,0 L100,0 L100,100 L0,100 L0,0"/>
      </svg>
    </section>