How to Capture CSS3 Animation Events in JavaScript

CSS3 animations are smooth and quick to implement but, unlike JavaScript, you don’t have frame-by-frame control. Fortunately, you can apply event handlers to any element to determine the animation state. This permits fine-grained control such as playing different animations in sequence.

Consider this simple CSS3 animation:


#anim.enable
{
	-webkit-animation: flash 1s ease 3;
	-moz-animation: flash 1s ease 3;
	-ms-animation: flash 1s ease 3;
	-o-animation: flash 1s ease 3;
	animation: flash 1s ease 3;
}

/* animation */
@-webkit-keyframes flash {
	50% { opacity: 0; }
}

@-moz-keyframes flash {
	50% { opacity: 0; }
}

@-ms-keyframes flash {
	50% { opacity: 0; }
}

@-o-keyframes flash {
	50% { opacity: 0; }
}

@keyframes flash {
	50% { opacity: 0; }
}

When the “enable” class is applied to the element with ID “anim”, the animation named flash is run three times. Each iteration lasts one second during which the element fades out then in.

Three types of event are fired when the animation occurs:

animationstart


var anim = document.getElementById("anim");
anim.addEventListener("animationstart", AnimationListener, false);

The animationstart event is fired when the animation starts for the first time.

animationiteration


anim.addEventListener("animationiteration", AnimationListener, false);

The animationiteration event is fired at the start of every new animation iteration, i.e. every iteration except the first.

animationend


anim.addEventListener("animationend", AnimationListener, false);

The animationend event is fired when the animation ends.

Browser Compatibility

At the time of writing, Firefox, Chrome, Safari, Opera and IE10 support CSS3 animation and the associated event handlers. In addition, Opera, IE10 and the webkit browsers use prefixes and throw in a few case changes for good measure…

W3C standard Firefox webkit Opera IE10
animationstart animationstart webkitAnimationStart oanimationstart MSAnimationStart
animationiteration animationiteration webkitAnimationIteration oanimationiteration MSAnimationIteration
animationend animationend webkitAnimationEnd oanimationend MSAnimationEnd

The easiest way around the prefix shenanigans is to call addEventListener for all prefixed and non-prefixed names using a custom function:


var pfx = ["webkit", "moz", "MS", "o", ""];
function PrefixedEvent(element, type, callback) {
	for (var p = 0; p < pfx.length; p++) {
		if (!pfx[p]) type = type.toLowerCase();
		element.addEventListener(pfx[p]+type, callback, false);
	}
}

Cross-browser event handlers can now be assigned using a single line of code:


// animation listener events
PrefixedEvent(anim, "AnimationStart", AnimationListener);
PrefixedEvent(anim, "AnimationIteration", AnimationListener);
PrefixedEvent(anim, "AnimationEnd", AnimationListener);

The Event Object

In the code above, the AnimationListener function is called whenever an animation event occurs. An event object is passed as a single argument. As well as the standard properties and methods, it also provides:

  • animationName: the CSS3 animation name (i.e. flash)
  • elapsedTime: the time in seconds since the animation started.

We could therefore detect when the flash animation ends, e.g.


if (e.animationName == "flash" && e.type.toLowerCase().indexOf("animationend") >= 0) {
	...
}

The code could, for example, remove the existing class or apply another CSS3 animation in a specific sequence.

View the CSS3 Animation Events in JavaScript demonstration

The demonstration page displays a button. When it’s clicked, an “enable” class is toggled which starts the flash animation. The state is displayed in a console when an animation event fires. When the animation ends, the “enable” class is removed so the button can be clicked again.

Let me know if you use animation event capturing in any interesting projects.

And if you enjoyed reading this post, you’ll love Learnable; the place to learn fresh skills and techniques from the masters. Members get instant access to all of SitePoint’s ebooks and interactive online courses, like HTML5 & CSS3 For the Real World.

Comments on this article are closed. Have a question about CSS? Why not ask it on our forums?

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://twitter.com/jokeyrhyme Ron

    Great tips, cheers!

    I’ve actually run into situations where the animation events never fire. I think this happens when the DOM is busy or has just inserted a large / complex fragment, etc. This happens in Google Chrome and iOS, so it might be WebKit-specific. If these events are on a critical path, make sure you have a setTimeout or something as a backup.

    I set a timeout for twice as long as the CSS Animation’s duration, and check to see (via a flag) whether or not the AnimationEnd event has been triggered. If the time is up and the event hasn’t gone off, then I forcefully continue execution.

    In general, I find events in WebKit to be pretty flaky, so use them sparingly and code defensively.

  • http://www.interativadesenvolvimento.com.br Diego Andrade

    On the table of browser compability, MSAnimationEnd is duplicated. But great article anyway!

  • rick

    “Flash” may have not been the best choice for the name of your sample. ;-)
    Good article otherwise.

  • http://www.americandigitalmediainc.com Jeff@americandigitalmedia

    Nice idea and tips for animation in CSS3. Hope this code can run on any browser with having a problem. I enjoy reading here.