2

I have some JavaScript that works fine on current versions of Chrome but doesn't work with Firefox. Event triggers for hover and click fail with Firefox, which I believe is a shadow DOM issue.

I can substitute the JavaScript hover code for CSS, which does work with Firefox (doesn't with Chrome) but this will not solve the issue for click events.

How can cross browser compatibility be achieved here?

let elementsArray = document.querySelectorAll(".icon");
elementsArray.forEach(function(elem) {
    console.log("getting element -"+String(elem));
    elem.addEventListener("mouseenter", function() {
      elem.setAttribute("style", " filter: grayscale(100%);");
    });
    elem.addEventListener("mouseout", function() {
      elem.setAttribute("style", "");
    });
    elem.addEventListener("click", function() {
      let hex = this.getAttribute('id');
      console.log(hex);
    });
});
 <svg 
   width="600"
   height="100" 
   >

<g id="layer0" class="slide" >
<path class="icon" id="y2011_0" d="M 285.0,45.0 300.0,70.98076211353316 285.0,96.96152422706632 255.0,96.96152422706632 240.0,70.98076211353316 255.0,45.0 z" fill="rgba(124,38,10,0.500000)"/>
<path class="icon" id="y2011_1" d="M 330.0,19.019237886466843 345.0,45.0 330.0,70.98076211353316 300.0,70.98076211353316 285.0,45.0 300.0,19.019237886466843 z" fill="rgba(124,39,10,0.500000)"/>
</g>
<use id="use" xlink:href="#layer0" href="#layer0" />
</svg>
Stumbler
  • 2,056
  • 7
  • 35
  • 61
  • can you not add the event listener to the use element? – Robert Longson Aug 02 '21 at 22:16
  • @RobertLongson that's a good point, but that will make all the path elements within that g trigger the event, as opposed to each one individually – Stumbler Aug 02 '21 at 22:20
  • Chrome doesn't adhere to the same rules firefox does. The element is supposed to create a non-exposed DOM. Its shadow root is set to "closed", in chrome that just doesn't seem to do very much. "closed" is supposed to "Deny access to the node(s) of a closed shadow root from JavaScript outside it" (https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow) In firefox this is true, the line `document.querySelectorAll(".icon")` only returns a nodelist of lenght 1, not including the elements in the shadow root. Chrome returns a list of len 2, including the shadow elements. – Sander Aug 03 '21 at 00:08

1 Answers1

2

A lot less code and more control if you create the SVG dynamically
with a W3C standard Web Component (JSWC supported in all modern Browsers)

<style>
  svg  { background: pink }
  path { stroke: blue }
</style>
<game-board>
  <hexagon x="46"  y="0"  />
  <hexagon x="138" y="0"  />
  <hexagon x="92"  y="26" />
  <hexagon x="138" y="52" />
  <hexagon x="92"  y="78" />
  <hexagon x="184" y="26" />
</game-board>
<script>
  customElements.define("game-board", class extends HTMLElement {
    connectedCallback() {
      setTimeout(() => {
        let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
        svg.innerHTML = `<style>` +
          `.hexagon{fill:red}.hexagon:hover{filter:grayscale(100%);cursor:pointer}` +
          `.hexclicked{fill:green}` +
          `</style>`;
        svg.append(...[...this.querySelectorAll("hexagon")].map(hex => {
          let path = document.createElementNS("http://www.w3.org/2000/svg", "path");
          path.setAttribute("d", `m${hex.attributes.x.value} ${hex.attributes.y.value} 15 26-15 26-30 0-15-26 15-26z`);
          path.classList.add("hexagon");
          path.onclick = (evt) => path.classList.toggle("hexclicked");
          return path;
        }));
        this.replaceWith(svg);
      })
    }
  })
</script>

<hexagon> is an UnknownElement; save to use,
see: https://dev.to/dannyengelman/web-components-using-unknownhtmlelements-for-better-semantic-html-5d8c

Danny '365CSI' Engelman
  • 16,526
  • 2
  • 32
  • 49