Detecting CSS Animation Completion with JavaScript

By on  

One fact of web development life in 2014 that's been difficult for me to admit is that the traditional JavaScript toolkit is mostly dead.  For years we relied on them for almost everything but now that JavaScript and CSS has caught up with what we need, we can often avoid using JavaScript toolkits if we take the time to research new native capabilities.  One argument for sticking with toolkits that I often hear is that CSS animations don't provide callback abilities.

Wrong.  False.  Incorrect.  ¿Que?  JavaScript does provide us the ability to trigger functionality at the end of JavaScript animations and transitions. Here's how!

The JavaScript

The only reason this is a somewhat involved task at this point is the need to account for browser prefixes.  The transitionend event and animationend is what standardized browsers require but WebKit-based browsers still rely on prefixes so we have to determine the prefix for the event, then apply it:

/* From Modernizr */
function whichTransitionEvent(){
    var t;
    var el = document.createElement('fakeelement');
    var transitions = {

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

/* Listen for a transition! */
var transitionEvent = whichTransitionEvent();
transitionEvent && e.addEventListener(transitionEvent, function() {
	console.log('Transition complete!  This is the callback, no library needed!');

	The "whichTransitionEvent" can be swapped for "animation" instead of "transition" texts, as can the usage :)

Once the animation or transition ends, the callback fires.  No need for a big library to assign callbacks to animations anymore!

Imageine how much in JavaScript code you can save by avoiding a JavaScript library for this.  The duration, fill-mode, and delay can all be set via CSS, so your JavaScript stays lightweight.  Major win!

Track.js Error Reporting

Recent Features

  • 9 Mind-Blowing Canvas Demos

    The <canvas> element has been a revelation for the visual experts among our ranks.  Canvas provides the means for incredible and efficient animations with the added bonus of no Flash; these developers can flash their awesome JavaScript skills instead.  Here are nine unbelievable canvas demos that...

  • 5 More HTML5 APIs You Didnât Know Existed

    The HTML5 revolution has provided us some awesome JavaScript and HTML APIs.  Some are APIs we knew we've needed for years, others are cutting edge mobile and desktop helpers.  Regardless of API strength or purpose, anything to help us better do our job is a...

Incredible Demos

  • jQuery Chosen Plugin

    Without a doubt, my least favorite form element is the SELECT element.  The element is almost unstylable, looks different across platforms, has had inconsistent value access, and disaster that is the result of multiple=true is, well, a disaster.  Needless to say, whenever a developer goes...

  • MooTools TwitterGitter Plugin

    Everyone loves Twitter. Everyone loves MooTools. That's why everyone should love TwitterGitter, a MooTools plugin that retrieves a user's recent tweets and allows the user to format them however the user would like. TwitterGitter allows the user to choose the number of...


  1. Hi David,

    Thanks for this post. I was trying Recently to implement a version of jQuery’s fadeIn and fadeOut using CSS animations. I experienced a certain amount of success by animating opacity. However I was stuck when trying to cleanly add and remove the element from the flow of the document. You can see my attempt here:


    I was wondering if you had any better ideas about how to achieve this?

  2. i use this:

    var standartransitionend = (!!window.webkitURL)?'webkitTransitionEnd':'transitionend';

    reasoning here: https://coderwall.com/p/ey5a1w

    • I guess checking window.WebKitAnimationEvent is slightly safer.

    • Using either of these is fraught with problems. The existence of prefixed URL global (or prefixed keyframe animations) has nothing to do with the existence of prefixed css transitions. These features are turned on, unprefixed, and deprecated separately so you don’t want to assume anything.

  3. Jay Merrifield

    One issue I’ve run into a number of times is the realization that like many events in javascript, transition end events bubble! So if you’re animating an 2 elements, one inside the other, the interior element could short circuit the exterior animation. Just to be safe, I also use the propertyName field which specifies which property finished it’s animation so you can make certain if you’re animating 2 properties on the same element you react to the correct transition end event.

    The code looks like this:

    el.addEventListener(transitionEndName, function(){
        if (e.target == el && (!propertyName || e.propertyName === propertyName)) {
            // this is your completion event!
  4. I just use all of the event name options… What’s wrong with that, other than filling up unnecessary object properties?

  5. JavaScript does provide us the ability to trigger functionality at the end of <> animations and transitions.

    Change to <>?

  6. Sorry. Another try.

    JavaScript does provide us the ability to trigger functionality at the end of **JavaScript”” animations and transitions.

    Change to **CSS**?

  7. Thanks! I used it in my plugin: https://github.com/jacoborus/nanobar

  8. Paolo

    in our project we had problems like the Samsung Galaxy S3 Mini stock browser wrongly returning ‘transitionend’ instead of ‘webkitTransitionEnd’, which forced us to resort to something like this: https://gist.github.com/depoulo/9dc8f59d791ec8f8e40c

  9. Paolo
  10. Thanks Maksim and Louis – will take a look!

  11. I believe Microsft never shipped any prefixed versions of this: http://msdn.microsoft.com/en-us/library/ie/hh673535(v=vs.85).aspx#transitions_dom_events

    Do you think it’s safe to remove the MSTransitionEnd bit?

    • I just did a little testing on the animationend event and IE browsers that support animations and transitions (IE > 10) don’t require the MS prefix. http://jsfiddle.net/U853f/3/

      Similarly, the latest versions of Firefox and Opera use animationend and webkitAnimationEnd. respectively. I suspect it’s the same for transitionend?

    • I just did a little testing on the animationend event and IE browsers that support animations and transitions (IE > 10) don’t require the MS prefix. http://jsfiddle.net/U853f/3/

      Similarly, the latest versions of Firefox and Opera use animationend
      and webkitAnimationEnd, respectively. I suspect it’s the same for
      transitionend ?

      (Reposted for proper formatting.)

  12. hamburger

    I’am using this jquery-snipe:

    $('#trigger').on('webkitTransitionEnd moztransitionend transitionend oTransitionEnd', function () {
        $('#debug').html('Animation ended');
    • I love this method. Yes, it uses jQuery, but looks so much more elegant… Thanks!

  13. Matt

    What is the importance of checking for the property name WebkitTransition instead of webkitTransition? I see they both exist, and point to the same string (=== reports true). However, why not just use webkitTransition as the property name?

  14. Beware that Webkit has a bug handling transitionEnd event for which if the CSS transitionEnd point would be equivalent to the element current point then the event would not be fired : http://stackoverflow.com/questions/2087510/callback-on-css-transition#answer-11354026

    I usually deal with this by setting a boolean check to be populated by the transitionEnd argument function which if still false, eg no event was fired, lets a setTimeout function do the same stuff

  15. I’ve struggled with using transitionEnd detection a couple of times recently – basically due to Chrome firing twice (on both transitionEnd and webkitTransitionEnd) – which was majorly messing it all up.

    In the function to detect which transition event if more that one event is supported would this return only one or both events?

  16. Paolo

    In my gist above it returns only the first supported event, due to the $.one() event listener, which clears itself after the first call.

  17. Carefull. It isn’t fired if the Element’s display becomes none.
    Btw, try to wrap the event in a Promise (I called mine addTransitionClass and removeTransitionClass). It’ s fantastic!

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!

Recently on David Walsh Blog

  • Convert Video to mp3

    Let's all be honest for a moment:  we've all ... not paid for ... music.  Whether it was via a file sharing app like Kazaa or Napster, or it was downloading and seeding on bittorrent, or maybe even downloading a music video and ripping its audio,...

  • Sort git Branches by Date

    I'll be first person to admit I don't do as much git repository maintenance as I should.  I rarely delete branches which have been merged, so a git branch execution shows me a mile-long list of branches that likely aren't relevant.  The best way to find branches I've recently...

  • Best Tools and Resources for Web Professionals in 2015

    Looking for the right resources to help you satisfy the needs of your clients? On the lookout for the best tools to help you increase your revenue? Searching for the right software to help you improve your business? Well, then you’ve come to the right place....

  • JavaScript Polling

    Polling with JavaScript is one of those ugly but important functions within advanced front-end user experience and testing practices.  Sometimes there isn't the event you can hook into to signify that a given task is complete, so you need to get your hands dirty and simply poll for...

  • OSCON Portland:  Conference Giveaway and Discount!

    O'Reilly puts on the best web industry conferences in the world.  These conferences include Fluent Conference, Velocity Conference, and the upcoming OSCON in Portland, Oregon from July 20-24.  Open Source Convention (OSCON) is a conference that focuses specifically on open source developers and the tools and possibilities...