'Trigger problem mouseover/mouseleave javascript

I'm trying to make an effect when the user switches elements on hover but I have an issue.

Here is my code

var oStudyRows = document.querySelectorAll('.study_row');

oStudyRows.forEach(function(oStudyRow) {
  oStudyRow.addEventListener('mouseover', function(e) {
    console.log('enter')
    oStudyRow.classList.add('blub');
  })
  oStudyRow.addEventListener('mouseleave', function(e) {
    oStudyRow.classList.remove('blub');
    console.log('out');
  })
})
<div class="wrapper_study">
  <div class="study_row study">
    <div class="study_year">aaaa</div>
    <div class="study_name">bbbb</div>
  </div>
  <div class="study_row work">
    <div class="study_year">aaaa</div>
    <div class="study_name">bbbbb</div>
  </div>
  <div class="study_row work">
    <div class="study_year">aaaa</div>
    <div class="study_name">bbbbbb</div>
  </div>
  <div class="study_row study">
    <div class="study_year">aaaa</div>
    <div class="study_name">bbbbb</div>
  </div>
  <div class="study_row work">
    <div class="study_year">aaaaa</div>
    <div class="study_name">bbbb</div>
  </div>
</div>

When I "enter" in the first row and I "leave" to the top , the enter/out work correctly, I have "enter/out" in the console but when I hover row1 to row2 I have a mutiple console log.

screenshot console

Someone can help me and explain to me why it doesn't work properly, please?

Thanks a lot



Solution 1:[1]

The children divs contained in .study_row will bubble the event to the parent. So to make only the parent fire the event, I set pointer-events to none for any children of .study_row.

There are better alternatives on setting the event handler here, but this is the solution taking the less modification to your strategy.

var oStudyRows = document.querySelectorAll('.study_row');

oStudyRows.forEach(function(oStudyRow){
    oStudyRow.addEventListener('mouseover', function(e){
        console.log('enter')
        oStudyRow.classList.add('blub');
    })
    oStudyRow.addEventListener('mouseleave', function(e){
        oStudyRow.classList.remove('blub');
        console.log('out');
    })
})
.study_row{
  border: solid 1px red;
  margin-bottom: 25px;
}

.study_row *{
  pointer-events: none;
}
<div class="wrapper_study">
   <div class="study_row study">
      <div class="study_year">aaaa</div>
      <div class="study_name">bbbb</div>
   </div>  
   <div class="study_row work">
      <div class="study_year">aaaa</div> 
      <div class="study_name">bbbbb</div>
   </div>  
   <div class="study_row work">
      <div class="study_year">aaaa</div> 
      <div class="study_name">bbbbbb</div>
   </div>  
   <div class="study_row study">
      <div class="study_year">aaaa</div>
      <div class="study_name">bbbbb</div>
   </div>  
   <div class="study_row work">
      <div class="study_year">aaaaa</div> 
      <div class="study_name">bbbb</div>
   </div>   
</div>

Here's a strategy using one single function to handle both the mouseover and mouseout events firing just on the .wrapper_study container element (and all of its children through bubbling .. excepts for elements that got pointer-events:none due to the rule .study_row *). To pursue this strategy I had to replace mouseleave with mouseout because the former one doesn't support bubbling.

You can find more on those topic here:

//one single event handler
function generalHandler(event){
  //element firing the event
  const eventTarget = event.target;
  //if eventTarget is a .study_row
  if(eventTarget.classList.contains('study_row')) {   
    //if event is mouseover
    if (event.type == 'mouseover'){
      eventTarget.classList.add('blub');
      //console.log('--enter');    
    }
    //if event is mouseout
    else if(event.type == 'mouseout'){
      eventTarget.classList.remove('blub');
      //console.log('--out');
    }      
  }
}

const mainContainer = document.querySelector('.wrapper_study');
mainContainer.addEventListener('mouseover', generalHandler);
mainContainer.addEventListener('mouseout', generalHandler);
.study_row{
  border: solid 1px red;
  margin-bottom: 25px;
  cursor: pointer;
}

.study_row *{
  pointer-events: none;
}

.blub{
  border-color: blue;
}
<div class="wrapper_study">
  <div class="study_row study">
    <div class="study_year">aaaa</div>
    <div class="study_name">bbbb</div>
  </div>
  <div class="study_row work">
    <div class="study_year">aaaa</div>
    <div class="study_name">bbbbb</div>
  </div>
  <div class="study_row work">
    <div class="study_year">aaaa</div>
    <div class="study_name">bbbbbb</div>
  </div>
</div>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1