jQuery onmouseenter and onmouseleave firing on position/size changes animation

I just known that jQuery onmouseenter / onmouseleave event will also fire every time the position/size of element changes during animation , so I use a simple validation by using if (img$.is(':animated')) return false; in the beginning of both event handlers.

But, this causes another problem, which is sometimes the mouseleave event will never been executed just because of the mouseleave has returned false early while the mouseenter isn’t finished yet.

$(function() {

  $("img[data-alt-src]").on('mouseenter', function(e) {
      e.stopPropagation();
      e.preventDefault();
      var img$ = $(e.currentTarget);
      if (img$.is(':animated')) return false; // the validation
      img$.finish().animate({
        opacity: '-=1.0',
        deg: '+=90'
      }, {
        duration: 250,
        step: function(now) {
          img$.css({
            '-moz-transform': 'rotateY(' + now + 'deg)',
            '-webkit-transform': 'rotateY(' + now + 'deg)',
            '-o-transform': 'rotateY(' + now + 'deg)',
            '-ms-transform': 'rotateY(' + now + 'deg)',
            transform: 'rotateY(' + now + 'deg)'
          });
        },
        complete: function() {
          img$.data('tmp-src', img$.attr('src'));
          img$.attr('src', img$.data('alt-src'));
        }
      });
      img$.animate({
        opacity: '+=1.0',
        deg: '-=90'
      }, {
        duration: 250,
        step: function(now) {
          img$.css({
            '-moz-transform': 'rotateY(' + now + 'deg)',
            '-webkit-transform': 'rotateY(' + now + 'deg)',
            '-o-transform': 'rotateY(' + now + 'deg)',
            '-ms-transform': 'rotateY(' + now + 'deg)',
            transform: 'rotateY(' + now + 'deg)'
          });
        }
      });
    })
    .on('mouseleave', function(e) {
      e.stopPropagation();
      e.preventDefault();
      var img$ = $(e.currentTarget);
      if (img$.is(':animated')) return false; // the validation
      img$.finish().animate({
        opacity: '-=1.0',
        deg: '+=90'
      }, {
        duration: 250,
        step: function(now) {
          img$.css({
            '-moz-transform': 'rotateY(' + now + 'deg)',
            '-webkit-transform': 'rotateY(' + now + 'deg)',
            '-o-transform': 'rotateY(' + now + 'deg)',
            '-ms-transform': 'rotateY(' + now + 'deg)',
            transform: 'rotateY(' + now + 'deg)'
          });
        },
        complete: function() {
          img$.attr('src', img$.data('tmp-src'));
        }
      });
      img$.animate({
        opacity: '+=1.0',
        deg: '-=90'
      }, {
        duration: 250,
        step: function(now) {
          img$.css({
            '-moz-transform': 'rotateY(' + now + 'deg)',
            '-webkit-transform': 'rotateY(' + now + 'deg)',
            '-o-transform': 'rotateY(' + now + 'deg)',
            '-ms-transform': 'rotateY(' + now + 'deg)',
            transform: 'rotateY(' + now + 'deg)'
          });
        }
      });
    });

});
img {
  width: 150px;
  height: 150px;
  margin-right: 1.5em;
}
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>

  <div style="position: relative; display: inline-block">
    <img
      src="http://mcenter.lazim.org/image/cache/catalog/demo/nikon_d300_1-270x270.jpg"
      data-alt-src="http://mcenter.lazim.org/image/cache/catalog/demo/nikon_d300_3-270x270.jpg"
      alt="">
    <img
      src="http://mcenter.lazim.org/image/cache/catalog/demo/nikon_d300_1-270x270.jpg"
      data-alt-src="http://mcenter.lazim.org/image/cache/catalog/demo/nikon_d300_3-270x270.jpg"
      alt="">
    <img
      src="http://mcenter.lazim.org/image/cache/catalog/demo/nikon_d300_1-270x270.jpg"
      data-alt-src="http://mcenter.lazim.org/image/cache/catalog/demo/nikon_d300_3-270x270.jpg"
      alt="">
    <img
      src="http://mcenter.lazim.org/image/cache/catalog/demo/nikon_d300_1-270x270.jpg"
      data-alt-src="http://mcenter.lazim.org/image/cache/catalog/demo/nikon_d300_3-270x270.jpg"
      alt="">
  </div>
  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
  
</body>
</html>

com-crop
I was get stucked how to get rid of this condition?

Hi @justinushermawan, if you just remove the .is(':animated') guard and the .finish() call inside the mouse leave event handler, the animation will get queued to run after the preceding animation has finished, regardless of when the event occurs. However I’d suggest to wrap the images in container divs and add the event listeners to those containers, so as to avoid accidentally triggering mouse enter / leave events by the transformation of the image.

Yes, it works if I wrap the img with a container and attach the event listener to the parent instead. I just realized that the mouseenter/mouseleave listener might be triggered if the animation changes the position/size of the element. So, it will need a container to deal with this case.

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.