Mouseenter fired multiple times in Firefox after moving node to another place in DOM

1k views Asked by At

I have a problem with mouseenter/mouseleave unexpected behavior in Firefox. The thing I need to do is:

  1. Move the node to another place in DOM after mouse enter has been registered on it
  2. Then move it back to original place after mouse leave has been registerd on it

The problem is that after inserting the node into another place, mouseenter fires like crazy and mouseleave doesn't fire at all.

Here's a pen illustrating that. You should inspect console output to see how many times mouseenter is fired. It might seem nonsensical in the pen, but in my app it isn't. I'm adding a zoom to the image on hover. One of it's ancestor's has overflow: hidden (which I cannot fight with CSS) and hides the enlarged portion of the image. So after the image is zoomed I need to move it into some other place in DOM (while making sure that it stays in the same place on the screen), then move it back in place when zoom is over (mouse leaves the image).

Question

Can someone explain to me what happens here? And how to fight it to achieve the regular mouseenter and mouseleave behavior (fired only once).

2

There are 2 answers

8
Joel Almeida On

First: Your variables outer1 and outer2 are the same.

Second, if you are changing the element of node it will trigger the mouseleave because the cursor is no longer on top of the element..

I think this is what you are trying to achieve:

var inner1 = document.getElementById("inner1");
var inner2 = document.getElementById("inner2");
var outer1 = document.getElementById("outer1");
var outer2 = document.getElementById("outer2");
var moved = false;

inner2.addEventListener("mouseenter", function(e) {
  if (moved) {
    zoomOut();
  } else {
    zoom();
  }
});

function zoom() {
  console.log("mouseenter");
  outer2.appendChild(inner1);
  moved = true;
}

function zoomOut() {
  console.log("mouseenter 2");
  outer1.appendChild(inner1);
  moved = false;
}
#outer1,
#outer2 {
  width: 300px;
  height: 200px;
}
#outer1 {
  background-color: green;
}
#outer2 {
  position: absolute;
  background-color: blue;
}
#inner2 {
  position: absolute;
  height: 70px;
  width: 100px;
  background-color: yellow;
}
<div id="outer1">
  <div id="inner1">
    <div id="inner2">Hover me</div>
  </div>
</div>
<div id="outer2">
</div>

As you can see I only listen to the mouseenter event because the mouseleave will always be triggered after you append the element. I added a flag: moved to figure out where the div is.

0
Partap Davis On

I'm having the same problem using D3 and a force-directed graph.

Each node has a mouseenter handler that calls this.parentNode.appendChild() to bring the element to the top. It's the only way I know of to rearrange SVG elements.

This works fine in Chrome and Safari, but in Firefox, it causes the looping mouseenter behavior. No mouseleave event is ever fired, either.

Best fix I have right now is to only call appendChild() when the node is not the topmost (node.nextSibling !== null)