Paul
Paul

Reputation: 123

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:

Upvotes: 1

Views: 78

Answers (2)

Yogi
Yogi

Reputation: 7324

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>

Upvotes: 7

Mister Jojo
Mister Jojo

Reputation: 22432

I think your problem comes from the fact that you are using the getElementsByClassName() method
use querySelector() or querySelectorAll() instead.

In your case, prefer to use the event delegation mechanism
(explained many times elsewhere here)

in HTML5 xmlns and xmlns:xlink= attributes are no longer in use

document.querySelector('svg').addEventListener('click', e =>
  {
  if (!e.target.matches('path.country')) return  // ignore other clicked

  console.log (`${e.target.getAttribute('title')} <> ${e.target.id}`);
  setTimeout(console.clear,2500);
  })
svg {
  width      : 300px;
  background : lightgrey;
  }
svg path.country {
  cursor : pointer;
  fill   : orange;
  }
svg path.country:hover {
  fill   : crimson;
  }
<svg viewBox="600 340 100 70">
  <path class="country" 
     title="United Arab Emirates"
     id="AE"
     d="M619.874,393.722L620.373,393.573L620.477,394.411L622.671,393.93
     L624.99,394.009L626.684,394.1L628.604,392.028L630.695,390.054
     L632.467,388.146L633.001,389.202L633.382,391.639L631.949,391.651
     L631.72,393.648L632.216,394.073L630.947,394.674L630.939,395.919
     L630.122,397.175L630.049,398.394L629.484,399.032L621.056,397.508
     L619.981,394.428z" />
  <path id="AF" 
     title="Afghanistan"
     class="country" 
     d="M646.879,356.901L649.738,358.201L651.853,357.745L652.438,356.188
     L654.651,355.669L656.231,354.617L656.791,351.832L659.154,351.154
     L659.594,349.902L660.917,350.843L661.762,350.952L663.323,350.975
     L665.438,351.716L666.295,352.143L668.324,351.017L669.27,351.694
     L670.174,350.085L671.85,350.159L672.281,349.641L672.578,348.213
     L673.785,346.975L675.303,347.785L674.998,348.869L675.846,349.038
     L675.585,351.994L676.694,353.137L677.672,352.404L678.916,352.057
     L680.663,350.486L682.594,350.745L685.486,350.751L685.985,351.758
     L684.353,352.15L682.928,352.795L679.71,353.2L676.699,353.931
     L675.063,355.439L675.725,356.899L676.046,358.603L674.649,360.033
     L674.766,361.335L673.995,362.549L671.328,362.444L672.43,364.663
     L670.646,365.507L669.455,367.511L669.609,369.491L668.514,370.415
     L667.477,370.109L665.334,370.537L665.027,371.451L662.939,371.446
     L661.377,373.289L661.278,376.039L657.635,377.374L655.682,377.092
     L655.114,377.794L653.438,377.386L650.634,377.865L645.936,376.228
     L648.479,373.298L648.249,371.202L646.125,370.65L645.905,368.565
     L644.987,365.921L646.187,364.094L644.966,363.599
     L645.736,361.148z" />
</svg>

Upvotes: -1

Related Questions