Andry
Andry

Reputation: 16845

Strange behavior of mouseover and mouseout events

I need to show an HTMLElement when hovering the mouse on an element (trigger) and hiding it when leaving the same element.

Working example

Here the shown/hidden element is not hovering the trigger element.

var div = document.createElement("div");
div.className = "d";
document.body.appendChild(div);

var hov = document.createElement("div");
hov.className = "h";

var hover = false;

var overEvent = "mouseenter";
var overOut = "mouseleave";

div.addEventListener(overEvent, function(e) {
  if (hover) return;
  
  document.body.appendChild(hov);
  hover = true;
  
  console.log("OVER");
});
div.addEventListener(overOut, function(e) {
  if (!hover) return;
  
  document.body.removeChild(hov);
  hover = false;
  
  console.log("OUT");
});
.d {
  width: 100px;
  height: 100px;
  background-color: #ff9900;
  z-index: 0;
}

.h {
  width: 100px;
  height: 100px;
  top: 150px;
  bottom: 150px;
  background-color: #000000;
  opacity: 0.5;
  z-index: 1;
}

div {
  position: absolute;
}

Not working

Here, the trigger is covered and things work strange:

var div = document.createElement("div");
div.className = "d";
document.body.appendChild(div);

var hov = document.createElement("div");
hov.className = "h";

var hover = false;

var overEvent = "mouseenter";
var overOut = "mouseleave";

div.addEventListener(overEvent, function(e) {
  if (hover) return;
  
  document.body.appendChild(hov);
  hover = true;
  
  console.log("OVER");
});
div.addEventListener(overOut, function(e) {
  if (!hover) return;
  
  document.body.removeChild(hov);
  hover = false;
  
  console.log("OUT");
});
.d {
  width: 100px;
  height: 100px;
  background-color: #ff9900;
  z-index: 0;
}

.h {
  width: 100%;
  height: 100%;
  background-color: #000000;
  opacity: 0.5;
  z-index: 1;
}

div {
  position: absolute;
}

In this case, I can see that the listeners are called repeatedly when moving the mouse on top of the trigger element, causing this funny intermittent undesired effect of the overlay to be placed and removed many times.

This happens also when using mouseover and mouseout as events.

Why is this happening?

Upvotes: 0

Views: 908

Answers (4)

user663031
user663031

Reputation:

Assuming hov follows div in DOM order, you can control its visibility with CSS.

var div = document.createElement("div");
div.className = "d";
document.body.appendChild(div);

var hov = document.createElement("div");
hov.className = "h";
document.body.appendChild(hov);
.d {
  width: 100px;
  height: 100px;
  background-color: #ff9900;
  z-index: 1;
}

.h {
  width: 100%;
  height: 100%;
  background-color: #000000;
  opacity: 0.5;
  display: none;
  z-index: 0;
}

div {
  position: absolute;
}

.d:hover + .h {
  display: block;
}

Upvotes: 0

Saerdn
Saerdn

Reputation: 228

In addition to the previous answers: You could also add the "remove div" event listener to the "hov" element instead to the "div" element. That would solve your problem right away. Here's the whole code plus some minor hints/improvements:

var div = document.createElement("div");
div.className = "d";
document.body.appendChild(div);

var hov = document.createElement("div");
hov.className = "h";

var hover = false;

var overEvent = "mouseenter";
var overOut = "mouseleave";

div.addEventListener(overEvent, function(e) {
    // if (hover) return; <-- you don't need that

    document.body.appendChild(hov);
    hover = !hover; // <-- that's easier to maintain incase you're changing the initial value

    console.log("OVER");
});
hov.addEventListener(overOut, function(e) {
    // if (!hover) return; <-- you don't need that

    document.body.removeChild(hov);
    hover = !hover;

    console.log("OUT");
});

Hint: Your hov-element currently covers the whole screen, you'll have to put it to "100px" instead of "100%" for a better experience:

.h {
    width: 100px;
    height: 100px;
    background-color: #000000;
    opacity: 0.5;
    z-index: 1;
}

Upvotes: 1

Salasar
Salasar

Reputation: 136

It happens because the upper element covers the element with handlers. Therefore, when the upper element occurs, the next mouse motion calls the handler for 'mouseleave'.

Upvotes: 0

Bruno Krebs
Bruno Krebs

Reputation: 3149

As Salasar already stated, this issue happens because the second div, the one that shows when hovering the first one, is showed exactly where the mouse is, and this triggers a mouse out event on the first div. What you can do to solve this issue, is configure your second div, the div.h, with 'pointer-events: none'. This will make it never to be a target of mouse events.

I have updated your example to show this working.

var div = document.createElement("div");
div.className = "d";
document.body.appendChild(div);

var hov = document.createElement("div");
hov.className = "h";

var hover = false;

var overEvent = "mouseenter";
var overOut = "mouseleave";

div.addEventListener(overEvent, function(e) {
  if (hover) return;
  
  document.body.appendChild(hov);
  hover = true;
  
  console.log("OVER");
});
div.addEventListener(overOut, function(e) {
  if (!hover) return;
  
  document.body.removeChild(hov);
  hover = false;
  
  console.log("OUT");
});
.d {
  width: 100px;
  height: 100px;
  background-color: #ff9900;
  z-index: 0;
}

.h {
  width: 100%;
  height: 100%;
  background-color: #000000;
  opacity: 0.5;
  z-index: 1;
  pointer-events: none;
}

div {
  position: absolute;
}

Upvotes: 1

Related Questions