105

Is it possible to get a notification (like callback) when a CSS transition has been completed?

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Pompair
  • 7,083
  • 11
  • 60
  • 69
  • http://stackoverflow.com/questions/5023514/how-do-i-normalize-css3-transition-functions-across-browsers – zloctb Mar 06 '15 at 11:52

5 Answers5

107

Yes, if such things are supported by the browser, then an event is triggered when the transition completes. The actual event however, differs between browsers:

  • Webkit browsers (Chrome, Safari) use webkitTransitionEnd
  • Firefox uses transitionend
  • IE9+ uses msTransitionEnd
  • Opera uses oTransitionEnd

However you should be aware that webkitTransitionEnd doesn't always fire! This has caught me out a number of times, and seems to occur if the animation would have no effect on the element. To get around this, it makes sense to use a timeout to fire the event handler in the case that it's not been triggered as expected. A blog post about this problem is available here: http://www.cuppadev.co.uk/the-trouble-with-css-transitions/ <-- 500 Internal Server Error

With this in mind, I tend to use this event in a chunk of code that looks a bit like this:

var transitionEndEventName = "XXX"; //figure out, e.g. "webkitTransitionEnd".. 
var elemToAnimate = ... //the thing you want to animate..
var done = false;
var transitionEnded = function(){
     done = true;
     //do your transition finished stuff..
     elemToAnimate.removeEventListener(transitionEndEventName,
                                        transitionEnded, false);
};
elemToAnimate.addEventListener(transitionEndEventName,
                                transitionEnded, false);

//animation triggering code here..

//ensure tidy up if event doesn't fire..
setTimeout(function(){
    if(!done){
        console.log("timeout needed to call transition ended..");
        transitionEnded();
    }
}, XXX); //note: XXX should be the time required for the
        //animation to complete plus a grace period (e.g. 10ms) 

Note: to get the transition event end name you can use the method posted as the answer in: How do I normalize CSS3 Transition functions across browsers?.

Note: this question is also related to: - CSS3 transition events

Community
  • 1
  • 1
Mark Rhodes
  • 10,049
  • 4
  • 48
  • 51
  • 2
    This is a much fuller answer than the accepted one. Why isn't this more upvoted. – Wes Jun 23 '13 at 09:29
  • Remember to call `transitionEndedHandler` in `transitionEnded` (or change `transitionEnded` by `transitionEndedHandler` in `addEventListener` and `removeEventListener` and call `transitionEnded` in `transitionEndedHandler`) – Miquel Jan 31 '15 at 13:52
  • Thanks @Miquel; think I'd just used `transitionEnded` when I meant `transitionEndedHandler`. – Mark Rhodes Jan 31 '15 at 18:31
  • I believe the Chromium issue for this can be found here: https://code.google.com/p/chromium/issues/detail?id=388082 Although, the last comment seems to (incorrectly in my view) discount it as a bug and therefore it is currently marked as "won't fix". – Willster Jun 11 '15 at 14:47
  • 1
    No need for different names nowadays https://caniuse.com/#feat=css-transitions – Ruan Mendes Feb 21 '20 at 12:41
83

I know that Safari implements a webkitTransitionEnd callback that you can attach directly to the element with the transition.

Their example (reformatted to multiple lines):

box.addEventListener( 
     'webkitTransitionEnd', 
     function( event ) { 
         alert( "Finished transition!" ); 
     }, false );
Cœur
  • 37,241
  • 25
  • 195
  • 267
Doug Neiner
  • 65,509
  • 13
  • 109
  • 118
  • is there the same thing for other browser then webkit? – meo Feb 27 '11 at 22:10
  • 7
    yes, 'transitionend' for Mozilla & 'oTransitionEnd' in Opera. – qqryq Mar 01 '11 at 13:14
  • 2
    what does the false at the end do? – meo Mar 15 '11 at 08:16
  • @meo It has something to do with the `this` element inside the callback. But its a required parameter, so in this case its just fulfilling the requirement. – Doug Neiner Mar 19 '11 at 17:23
  • 9
    @DougNeiner The false at the end is for `useCaptureMode`. When an event occurs, there are two phases - the first phase is capture mode, the second is bubble mode. In capture mode, the event descends from the body element to the specified element. It then enters bubble mode, where it does the reverse. That final false param specifies that you want the event listener to occur in bubble mode. One use of this is to attach event handlers right before they are needed in bubble mode. =) http://37signals.com/svn/posts/3137-using-event-capturing-to-improve-basecamp-page-load-times – rybosome Jul 29 '12 at 18:31
  • It's only a required parameter in Firefox I believe – danwellman Feb 24 '15 at 17:34
43

I am using the following code, is much simpler than trying to detect which specific end event a browser uses.

$(".myClass").one('transitionend webkitTransitionEnd oTransitionEnd otransitionend MSTransitionEnd', 
function() {
 //do something
});

Alternatively if you use bootstrap then you can simply do

$(".myClass").one($.support.transition.end,
function() {
 //do something
});

This is becuase they include the following in bootstrap.js

+function ($) {
  'use strict';

  // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
  // ============================================================

  function transitionEnd() {
    var el = document.createElement('bootstrap')

    var transEndEventNames = {
      'WebkitTransition' : 'webkitTransitionEnd',
      'MozTransition'    : 'transitionend',
      'OTransition'      : 'oTransitionEnd otransitionend',
      'transition'       : 'transitionend'
    }

    for (var name in transEndEventNames) {
      if (el.style[name] !== undefined) {
        return { end: transEndEventNames[name] }
      }
    }

    return false // explicit for ie8 (  ._.)
  }

  // http://blog.alexmaccaw.com/css-transitions
  $.fn.emulateTransitionEnd = function (duration) {
    var called = false, $el = this
    $(this).one($.support.transition.end, function () { called = true })
    var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
    setTimeout(callback, duration)
    return this
  }

  $(function () {
    $.support.transition = transitionEnd()
  })

}(jQuery);
Tom
  • 12,591
  • 13
  • 72
  • 112
  • 2
    This is slower because it has to search through that entire list each time the event happens to see if it is the correct one. – phreakhead Feb 13 '14 at 07:07
  • 3
    @phreakhead The performances of any of these approaches will be very similar – Tom May 13 '14 at 15:31
11

This can easily be achieved with the transitionend event. See documentation here.

A simple example:

document.getElementById("button").addEventListener("transitionend", myEndFunction);

function myEndFunction() {
    this.innerHTML = "Transition event ended";
}
#button {transition: top 2s; position: relative; top: 0;}
<button id="button" onclick="this.style.top = '55px';">Click me to start animation</button>
djvg
  • 11,722
  • 5
  • 72
  • 103
Yehuda Schwartz
  • 3,378
  • 3
  • 29
  • 38
  • 5
    This is the correct answer nowadays, there's no need to have prefixes anymore. https://caniuse.com/#feat=css-transitions – Ruan Mendes Feb 21 '20 at 12:42
  • 1
    or https://developer.mozilla.org/en-US/docs/Web/API/Element/transitionend_event#browser_compatibility – djvg Mar 30 '23 at 13:45
6

The jQuery.transit plugin, a plugin for CSS3 transformations and transitions, can call your CSS animations from script and give you a callback.

T. Junghans
  • 11,385
  • 7
  • 52
  • 75
Brian
  • 2,819
  • 4
  • 24
  • 30