8

I have two CSS @keyframe animations applied to the same element. One that fires on :hover and the other that fires on mouse out, both applied with CSS.

I was curious to know if there was a way to detect the end of a selected keyframe animation rather than it being attached to the element and firing twice?

8
  • You can only detect when an animation ends, not a single keyframe. Commented Feb 5, 2016 at 10:44
  • @MarcosPérezGude At first i understood question as you, but now i think OP just wants to detect just specific animation ends, not for each keyframe. So Harry's answer is correct Commented Feb 5, 2016 at 10:55
  • Both answers are fine in this case. The first answer is correct too. I upvote both of them Commented Feb 5, 2016 at 10:57
  • 1
    @MarcosPérezGude but in other answer, no filtering is done regarding which animation completes. I really guess this is all the point of this question here: One that fires on :hover and the other that fires on mouse out & detect the end of a selected keyframe animation rather than it being attached to the element and firing twice Commented Feb 5, 2016 at 11:10
  • 1
    For the fun (chrome only), if your question was to detect current running keyframe: jsfiddle.net/z4ytsesh And here using an animated property to get progress in percent jsfiddle.net/z4ytsesh/1 I'm sure this is offtopic but... Commented Feb 5, 2016 at 12:34

2 Answers 2

9

if there was a way to detect the end of a selected keyframe animation

If your intention is to detect the ending of a keyframe animation itself instead of detect end of every keyframe then, yes, it can be done using the animationend event. This event is fired every time any animation that is attached to the element is completed and the context info has one parameter named animationName using which we can find which animation had ended.

The animationName parameter is important because when multiple animations would be applied to the same element like in your case then you'd need to know which animation had actually ended because this event would get fired at the end of every animation.

Using vanilla JS:

window.onload = function() {
  var elm = document.querySelector('.animate');
  var op = document.querySelector('.output');

  elm.addEventListener('animationend', function(e) { /* this is fired at end of animation */
    op.textcontent = 'Animation ' + e.animationName + ' has ended';
  });
  elm.addEventListener('animationstart', function(e) { /* this is fired at start of animation */
    op.textcontent = 'Animation ' + e.animationName + ' has started';
  });
}
.animate {
  height: 100px;
  width: 100px;
  margin: 10px;
  border: 1px solid red;
  animation: shake-up-down 2s ease;
}
.animate:hover {
  animation: shake-left-right 2s ease forwards;
}
@keyframes shake-up-down {
  0% {
    transform: translateY(0px);
  }
  25% {
    transform: translateY(10px);
  }
  75% {
    transform: translateY(-10px);
  }  
  100% {
    transform: translateY(0px);
  }  
}
@keyframes shake-left-right {
  0% {
    transform: translateX(0px);
  }
  25% {
    transform: translateX(10px);
  }
  75% {
    transform: translateX(-10px);
  }  
  100% {
    transform: translateX(0px);
  }  
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class='animate'></div>

<div class='output'></div>


Using jQuery:

$(document).ready(function() {
  var elm = $('.animate');
  var op = $('.output');

  elm.on('animationend', function(e) { /* fired at the end of animation */
    op.html('Animation ' + e.originalEvent.animationName + ' has ended');
  });
  elm.on('animationstart', function(e) { /* fired at the start of animation */
    op.html('Animation ' + e.originalEvent.animationName + ' has started');
  });  
});
.animate {
  height: 100px;
  width: 100px;
  margin: 10px;
  border: 1px solid red;
  animation: shake-up-down 2s ease;
}
.animate:hover {
  animation: shake-left-right 2s ease forwards;
}
@keyframes shake-up-down {
  0% {
    transform: translateY(0px);
  }
  25% {
    transform: translateY(10px);
  }
  75% {
    transform: translateY(-10px);
  }
  100% {
    transform: translateY(0px);
  }
}
@keyframes shake-left-right {
  0% {
    transform: translateX(0px);
  }
  25% {
    transform: translateX(10px);
  }
  75% {
    transform: translateX(-10px);
  }
  100% {
    transform: translateX(0px);
  }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class='animate'></div>

<div class='output'></div>

In the above snippet, you can see how the .output div's content indicates the name of the animation that is ended after each animation completes.

Note: CSS animations still need vendor prefixes in some browsers/versions. To be on the safer side, you need to listen for the prefixed versions of the animationend event also.

Sign up to request clarification or add additional context in comments.

Comments

1

Try this example:

function whichAnimationEvent(){
  var t,
  el = document.createElement("fakeelement");

 var animations = {
   "animation"      : "animationend",
   "OAnimation"     : "oAnimationEnd",
   "MozAnimation"   : "animationend",
   "WebkitAnimation": "webkitAnimationEnd"
 }

for (t in animations){
  if (el.style[t] !== undefined){
    return animations[t];
  }
 }
}

var animationEvent = whichAnimationEvent();

$(".button").click(function(){
$(this).addClass("animate");
$(this).one(animationEvent,
          function(event) {
  // Do something when the animation ends
 });
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.