-1

I'm creating a visual counter in my html using Javascript. At first the counter is '0'. On mouseenter it starts counting till 10 (adding +1 every second), and on moueseleave it should reset to 0 again and stop counting, however, when I move the cursor away it only shortly resets to 0, and then keeps counting up to 10 again, starting from the number it stopped at during the mouseleave.

My code looks like this:

let timer = ''

box[4]?.addEventListener('mouseenter', () => {
    let sec = 0
    let timer = setInterval(() => {
        box[4].innerHTML = String(1 + sec)
        sec++
        if(sec === 10){
            clearInterval(timer);
        }       
    }, 1000)   
})  

box[4]?.addEventListener('mouseleave', () => {
    clearInterval(timer)
    box[4].innerHTML = String(0) 
})  

How can I improve it so that on mouseleave the counter becomes 0 again and stops any counting?

Zixxy7
  • 71
  • 4
  • If the answer from @adsy doesn't immediately enlighten you, you definitely should learn about [scope](https://github.com/getify/You-Dont-Know-JS/blob/2nd-ed/scope-closures/ch2.md). Scope is a fundamental concept in coding and it is very important that you understand at least tha basics. Spoiler: The basics really are easy to grasp. – RSeidelsohn Jan 14 '23 at 22:26
  • It does. Was an honest mistake. – Zixxy7 Jan 14 '23 at 22:28

3 Answers3

2

You need to remove the let on let timer inside the mouseenter callback:

let timer = ''

box[4]?.addEventListener('mouseenter', () => {
    let sec = 0
    timer = setInterval(() => {
        box[4].innerHTML = String(1 + sec)
        sec++
        if(sec === 10){
            clearInterval(timer);
        }       
    }, 1000)   
})  

box[4]?.addEventListener('mouseleave', () => {
    clearInterval(timer)
    box[4].innerHTML = String(0) 
})  

The reason is that when you do let timer you are actually populating a new local var called timer and the other var at the top of the code will not be populated. That means your clearInterval inside of the mouseleave handler was not actually canceling anything.

adsy
  • 8,531
  • 2
  • 20
  • 31
1
 let timer = setInterval(() => {
        box[4].innerHTML = String(1 + sec)
        sec++
        if(sec === 10){
            clearInterval(timer);
        }       
    }, 1000)   

On this part of the code you are setting up timer again and again. If you delete the keyword let it will work perfecly.

Laex
  • 11
  • 1
0

This isn't exactly what you asked but it may be useful in some cases, at least, to know that you can do all of this in the one listener. Whether it is best for your specific case, I don't know; but have used the pattern often for drag events. It keeps the two events in the same scope and the mouseleave event "active" only after a mouseenter event and removes it immediately afterward.

let box4 = document.querySelector('.box4');

box4.addEventListener('mouseenter', () => {
    let sec = 0;
    let timer = setInterval(() => {
        box4.innerHTML = String(1 + sec)
        sec++
        if(sec === 10){
            clearInterval(timer);
        }       
    }, 1000)   

  box4.addEventListener('mouseleave', clearBox4);
  
  function clearBox4 () {
    clearInterval(timer);
    box4.removeEventListener('mouseleave',clearBox4);    
    box4.innerHTML = String(0);
    console.log('Removed listener mouseenter on box4 at ' + sec + ' seconds.');
  }
})  

  
.box4 {
 width: 100px;
 height: 100px;
 background-color: blue;
 font-size: 30px;
 color: white;
 padding-top: 15px;
 text-align: center;
 }
<div class="box4"></div>
Gary
  • 2,393
  • 12
  • 31