John Jasper
John Jasper

Reputation: 13

onmouseenter event triggered on any movement in an SVG use element

When the "mouseenter" event is triggered on the green circle a 1 is printed in the console, when it is triggered on the blue circle a 2 is printed in the console. Note that when the mouse enters the green circle, 1 is printed exactly once. When the mouse enters the blue circle any mouse movement within the circle causes a 2 to be printed.

        var gr = document.getElementById("greenOne");
        var bl = document.getElementById("blueOneCopy");
        gr.onmouseenter = function() {gr.parentNode.appendChild(gr); console.log(1);};
        bl.onmouseenter = function() {bl.parentNode.appendChild(bl); console.log(2);};
<html>
  <head>
    <script src="https://d3js.org/d3.v3.min.js"></script>
  </head>
  <body>
    <svg height="500px" width="500px">
      <defs>
      <circle id="blueOne" cx="100" cy="100" r="50" style="fill: #0091EA"></circle>
      </defs>
      <use id="blueOneCopy" href="#blueOne"></use>
      <circle id="greenOne" cx="150" cy="100" r="50" style="fill: #00C853"></circle>
    </svg>
  </body>
</html>

Upvotes: 1

Views: 519

Answers (2)

Paul LeBeau
Paul LeBeau

Reputation: 101820

Another option is to check whether the element is the last one in the SVG before moving it there. See below

I expect that this issue will have something to do with how events behave when Shadow DOM elements are attached to the DOM. Since Chrome and Firefox behave the same, I expect it is behaviour described in the spec. I'm just too lazy right now to check :)

Note that, with both mine and RFoxtea's solutions, the mouseenter event is still being fired. We are just detecting the first occurrence. Be careful you don't slow down your app by putting code in the event that unnecessarily runs every time.

var gr = document.getElementById("greenOne");
var bl = document.getElementById("blueOneCopy");
gr.onmouseenter = bringToFront;
bl.onmouseenter = bringToFront;

function bringToFront(evt) {
  let el = evt.target;
  if (el.nextSibling != null) {
    el.parentNode.appendChild(el);
    console.log(el.id == 'greenOne' ? 1 : 2);
  }
};
<html>
  <head>
    <script src="https://d3js.org/d3.v3.min.js"></script>
  </head>
  <body>
    <svg height="500px" width="500px">
      <defs>
      <circle id="blueOne" cx="100" cy="100" r="50" style="fill: #0091EA"></circle>
      </defs>
      <use id="blueOneCopy" href="#blueOne"></use>
      <circle id="greenOne" cx="150" cy="100" r="50" style="fill: #00C853"></circle>
    </svg>
  </body>
</html>

Upvotes: 0

Raf Vost&#233;
Raf Vost&#233;

Reputation: 128

I've managed to get blueOneCopy to behave similarly to greenOne by checking whether the relatedTarget property of the mouse event isn't blueOne before executing the rest of the function. I'm not sure if this is the best solution, but it appears to work.

var gr = document.getElementById("greenOne");
var bl = document.getElementById("blueOneCopy");
gr.onmouseenter = function() {
  gr.parentNode.appendChild(gr);
  console.log(1);
};
bl.onmouseenter = function(e) {
  if (e.relatedTarget.id !== "blueOne") {
    bl.parentNode.appendChild(bl);
    console.log(2);
  }
};
<html>

<head>
  <script src="https://d3js.org/d3.v3.min.js"></script>
</head>

<body>
  <svg height="500px" width="500px">
      <defs>
      <circle id="blueOne" cx="100" cy="100" r="50" style="fill: #0091EA"></circle>
      </defs>
      <use id="blueOneCopy" href="#blueOne"></use>
      <circle id="greenOne" cx="150" cy="100" r="50" style="fill: #00C853"></circle>
    </svg>
</body>

</html>

Upvotes: 1

Related Questions