简体   繁体   中英

JavaScript - Attaching event handlers to an element also attaches them to its children

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?

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> 

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> 

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> 

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM