Search code examples
javascriptsettimeoutaddeventlistenernodelistcleartimeout

Adding eventListeners in a loop (mouseenter) but only triggering after 1500ms - issue with clearTimeout


I have a grid of images, which users can mouseover to preview. I am able to add a listener to each one in a loop. I would only like to trigger my logic if the user is over the item for at least 1.5 seconds. I can't determine why my mouseleave event which attempts to clearTimeout of the timer, is not actually clearing it.

I have added comments in the code to also clarify:

(function () {
  var nodeList = document.querySelectorAll('div.MyDiv > img') || undefined;
  if (nodeList) {
    nodeList.forEach(function (_el, idx) {
      _el.addEventListener("mouseenter", function (e) {
        var _imgsrcga = e.srcElement.src;
         var timer = setTimeout(function() {
            console.log('This ran'); // this is OK after 1.5 seconds, but it ALWAYS runs after 1.5 seconds
        }, 1500)
      }, false);

      // not clearing the timer....
      _el.addEventListener("mouseleave", function(e) {
        console.log('cleared')
        clearTimeout(timer) // my timer never clears with a mouseleave event
      })
    })
  }
})();

So: my console.log('this ran') does in fact delay 1.5 seconds, but I can never get rid of my timer if they mouseleave in < 1.5 seconds.

Thanks


Solution

  • Declare the timer outside the function in order to be accesible from other functions.

    (function () {
      var timer;
      var nodeList = document.querySelectorAll('div.MyDiv > img') || undefined;
      if (nodeList) {
        nodeList.forEach(function (_el, idx) {
          _el.addEventListener("mouseenter", function (e) {
            var _imgsrcga = e.srcElement.src;
             timer = setTimeout(function() {
                console.log('This ran'); 
            }, 1500)
          }, false);
    
          _el.addEventListener("mouseleave", function(e) {
            console.log('cleared')
            clearTimeout(timer)
          })
        })
      }
    })();
    <div class="MyDiv">
      <img src="http://placekitten.com/g/200/300">
    </div>

    I include the way i would write this piece of code. In my opinion, your code could be structured differently, for the sake of readability & understanding. I've removed some unnecessary lines too, and used ES6 syntax.

    (function () {
      let timer;
      let nodeList = document.querySelectorAll('div.MyDiv > img');
    
      const mouseOver = e=> {
        let _imgsrcga = e.target.src;
        timer = setTimeout( ()=> console.log('This ran'), 1500)
      };
      const mouseOut = e=> {
        console.log('cleared')
        clearTimeout(timer)
      };
        
      nodeList.forEach( _el=>{
        _el.addEventListener("mouseover", mouseOver , false);
        _el.addEventListener("mouseout", mouseOut);
      });
      
    })();
    <div class="MyDiv">
      <img src="http://placekitten.com/g/200/300">
    </div>

    Notes :

    • use mouseover and mouseout events instead
    • don't use e.srcElement, use e.target (standard)
    • don't need to check nodeList after querySelectionAll (worst case, you'll have an empty list, and forEach will do nothing)
    • don't declare inline event handlers (better performance)