0

I have this HTML. I want to attach mouseover and mouseleave events ONLY to the .parent element, but for some reason they also get attached to the children elements and I get weird behaviour.

<div class="parent">
  <div class="child-1"></div>
  <div class="child-2"></div>
</div>

Here's how I do it

const parent = document.getElementsByClassName('parent')[0]
parent.addEventListener('mouseover', eventHandler)
parent.addEventListener('mouseleave', eventHandler)

Whats going on and how to prevent it?

Sartheris Stormhammer
  • 2,534
  • 8
  • 37
  • 81
  • @misorude isnt event bubbling only upwards? – Sartheris Stormhammer Nov 26 '18 at 09:58
  • 1
    That combination of event handlers doesn’t make too much sense to begin with. The counterpart to mouseover would be mouseout, and the counterpart to mouseleave would be mouseenter. Make sure you know the difference in their behavior. You usually want to use the right combination, not mix them like this. – misorude Nov 26 '18 at 10:01

4 Answers4

1

You have to add event to child nodes and cancel the propagation of the event.

const parent = document.getElementsByClassName('parent')[0]
parent.addEventListener('mouseover', eventHandler)
parent.addEventListener('mouseleave', eventHandler)

for (const child of parent.childNodes) {
  child.addEventListener('mouseover', function(event) {
    event.stopPropagation();
  })
  child.addEventListener('mouseleave', function(event) {
    event.stopPropagation();
  })
}

function eventHandler() {
  console.log('hey');
}
<div class="parent" style="width: 25px; height: 25px; background: red; padding: 50px">
  <div class="child-1" style="width: 25px; height: 25px; background: blue"></div>
  <div class="child-2" style="width: 25px; height: 25px; background: green"></div>
</div>

Or you could do what everyone wants pointer-event

const parent = document.getElementsByClassName('parent')[0]
parent.addEventListener('mouseover', eventHandler)
parent.addEventListener('mouseleave', eventHandler)

for (const child of parent.childNodes) {
  child.className += ' no-event'
}

function eventHandler() {
  console.log('hey');
}
.no-event {
  pointer-events: none;
}
<div class="parent" style="width: 25px; height: 25px; background: red; padding: 50px">
  <div class="child-1" style="width: 25px; height: 25px; background: blue"></div>
  <div class="child-2" style="width: 25px; height: 25px; background: green"></div>
</div>
ACD
  • 1,431
  • 1
  • 8
  • 24
0

It has to do how event catching and bubbling works. I copied the @ACD answer to see that it does behave differently. If you notice if we hover on child and move out to the parent the event is not called in this example. So child elements are no more the target of the event.

There are two phases of the event, Catching and Bubbling. You can find more information here. What is happening that event is triggered for the child and it is bubbling to the parent. You can check the event target via event.target

If we remove pointer-events: none; then the target is the child not the parent . and after the child handler is called the vent is propagated to the parent and then the parents callback is called.

const parent = document.getElementsByClassName('parent')[0]
parent.addEventListener('mouseover', eventHandler)
parent.addEventListener('mouseleave', eventHandler)

 

function eventHandler(e) {
  console.log(e.target, 'hey');
}
<div class="parent" style="width: 25px; height: 25px; background: red; padding: 50px">
  <div class="child-1" style="pointer-events: none; width: 25px; height: 25px; background: blue"></div>
  <div class="child-2" style="pointer-events: none; width: 25px; height: 25px; background: green"></div>
</div>
kodvin
  • 1,236
  • 9
  • 14
  • imagine you have thousands of child element with different classes and tag types. ain't that dirty? – ACD Nov 26 '18 at 10:15
  • It is just an example to show that it is more to event handling. Your solution is OK, but i just wanted to add more context, explain why. Besides a simple css selector can be written to add for all children, so to answer the question it will not get dirtier than stoping propagation for all children. (jus to iterate it to complement your response and not to dismiss it) – kodvin Nov 26 '18 at 11:27
-1

Try mouseenter and mouseleave instead of mouseover and mouseleave. Don't mix them up. It's either the combination of mouseover/mouseout or mouseenter/mouseleave, because these combos are symmetrical.

https://developer.mozilla.org/en-US/docs/Web/Events/mouseenter

var logs = document.querySelector('.log');
var area = document.querySelector('.parent');

function eventHandler (e) {
  if (e.type === 'mouseenter') {
    area.style.backgroundColor = '#c00';
  } else {
    area.style.backgroundColor = '';
  }
}

area.addEventListener('mouseenter', eventHandler);
area.addEventListener('mouseleave', eventHandler);
.parent {
  background: #eee;
}
.child {
  padding: 30px 20px;
}
.child + .child {
  border-top: 1px dotted #333;
}
<div class="parent">
  <div class="child">
    foo bar baz
  </div>
  <div class="child">
    foo bar baz
  </div>
</div>
David
  • 3,552
  • 1
  • 13
  • 24
-1

You could fix this with css, by applying

.child-1, .child-2 {
  pointer-events: none
}

to the children, that way no interaction with the children is possible.

Alternatively, in the eventhandler you could check if the event.target is the parent or a childnode, hovering over a childnode will yield the childnode, hovering over .parent will yield the parent.

Ids Klijnsma
  • 442
  • 3
  • 9