How do I normalize the CSS3 transition end events across browsers?

38,368

Solution 1

There's a technique used in Modernizr, improved:

function transitionEndEventName () {
    var i,
        undefined,
        el = document.createElement('div'),
        transitions = {
            'transition':'transitionend',
            'OTransition':'otransitionend',  // oTransitionEnd in very old Opera
            'MozTransition':'transitionend',
            'WebkitTransition':'webkitTransitionEnd'
        };

    for (i in transitions) {
        if (transitions.hasOwnProperty(i) && el.style[i] !== undefined) {
            return transitions[i];
        }
    }

    //TODO: throw 'TransitionEnd event is not supported in this browser'; 
}

Then you can just call this function whenever you need the transition end event:

var transitionEnd = transitionEndEventName();
element.addEventListener(transitionEnd, theFunctionToInvoke, false);

Solution 2

As per Matijs comment, the easiest way to detect transition events is with a library, jquery in this case:

$("div").bind("webkitTransitionEnd.done oTransitionEnd.done otransitionend.done transitionend.done msTransitionEnd.done", function(){
  // Unlisten called events by namespace,
  // to prevent multiple event calls. (See comment)
  // By the way, .done can be anything you like ;)
  $(this).off('.done')
});

In library-less javascript it gets a bit verbose:

element.addEventListener('webkitTransitionEnd', callfunction, false);
element.addEventListener('oTransitionEnd', callfunction, false);
element.addEventListener('transitionend', callfunction, false);
element.addEventListener('msTransitionEnd', callfunction, false);

function callfunction() {
   //do whatever
}

Solution 3

If you use jQuery and Bootstrap $.support.transition.end will return the right event for the current browser.

It is defined in Bootstrap and used in its animation callbacks, though the jQuery docs say not to rely on these properties:

Although some of these properties are documented below, they are not subject to a long deprecation/removal cycle and may be removed once internal jQuery code no longer needs them.

http://api.jquery.com/jQuery.support/

Solution 4

Update

The following is a cleaner way of doing it, and doesn't require modernizr

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

Alternatively

var transEndEventNames = {
        'WebkitTransition': 'webkitTransitionEnd',
        'MozTransition': 'transitionend',
        'OTransition': 'oTransitionEnd otransitionend',
        'msTransition': 'MSTransitionEnd',
        'transition': 'transitionend'
    }, transitionEnd = transEndEventNames[Modernizr.prefixed('transition')];

This is based on the code suggested by Modernizr, but with the extra event for newer versions of Opera.

http://modernizr.com/docs/#prefixed

Solution 5

As of 2015, this one-liner should do the deal (IE 10+, Chrome 1+, Safari 3.2+, FF 4+ and Opera 12+):-

var transEndEventName = ('WebkitTransition' in document.documentElement.style) ? 'webkitTransitionEnd' : 'transitionend'

Attaching the event listener is simple:-

element.addEventListener(transEndEventName , theFunctionToInvoke);
Share:
38,368
methodofaction
Author by

methodofaction

I'm a generalist who likes to design and code. I make things. I speak Spanish and English. If you have a small vanilla JS, CSS or SVG challenge that you'd like me to tackle, I'm available for 0.5 - 5 hour projects at an affordable rate, get in touch with me at [email protected] My creative endeavor: https://method.ac

Updated on January 07, 2022

Comments

  • methodofaction
    methodofaction over 2 years

    Webkit's transition end event is called webkitTransitionEnd, Firefox is transitionEnd, opera is oTransitionEnd. What is a good way of tackling all of them in pure JS? Should I do browser sniffing? or implement each one separately? Some other way that hasn't occurred to me?

    i.e.:

    //doing browser sniffing
    var transitionend = (isSafari) ? "webkitTransitionEnd" : (isFirefox) ? "transitionEnd" : (isOpera) ? "oTransitionEnd";
    
    element.addEventListener(transitionend, function(){
      //do whatever
    },false);
    

    or

    // Assigning an event listener per browser
    element.addEventListener("webkitTransitionEnd", fn);
    element.addEventListener("oTransitionEnd", fn);
    element.addEventListener("transitionEnd", fn);
    
    function fn() {
       //do whatever
    }
    
  • methodofaction
    methodofaction about 13 years
    Thanks! I ended up doing something similar, but without browser sniffing. You can see the result (and code) here: cssglue.com/cubic. The only problem with your solution is that—if browser vendors decide to standarize their transition events—they might drop their prefixes and they would stop working (unlikely, yet). But yes, it makes the code much more cleaner.
  • Rich Bradshaw
    Rich Bradshaw about 13 years
    I agree, I've been meaning to replace mine with something better, but on the other hand I like the simplicity of it.
  • Matijs
    Matijs about 13 years
    For what it's worth. This can be done without browser sniffing by just doing object.bind('transitionend oTransitionEnd webkitTransitionEnd', function() { // callback } );
  • vieron
    vieron over 11 years
    oTransitionEnd was lowercased to otransitionend in Opera. See opera.com/docs/specs/presto2.10/#m274
  • gossi
    gossi over 11 years
    it also is transitionend in all lowercase now. See dev.w3.org/csswg/css3-transitions/#transition-events
  • wwaawaw
    wwaawaw over 11 years
    That second-to-last one shouldn't be camelCased.
  • alt
    alt over 11 years
    Yeah, to get this to work in Firefox now 'transition':'transitionEnd' needs to be 'transition':'transitionend'
  • alt
    alt over 11 years
    This is a great way to do it but it requires Modernizr. Can this be written simply but without Modernizr?
  • redochka
    redochka over 11 years
    Also you can remove 'MSTransition':'msTransitionEnd' as Internet explorer 10 support standard syntax and internet explorer 9 doesn't support transitionEnd event at all. I will try to edit the answer.
  • Aurelio
    Aurelio over 11 years
    For some reason this code failed in FF for me. Solved the issue thanks to this page marakana.com/static/bookshelf/css3_transitions_tutorial/…
  • mgol
    mgol over 11 years
    The non-prefixed version of the event is named transitionend, not TransitionEnd.
  • webinista
    webinista over 11 years
    I removed the MsTransition bit, but will leave the rest of the answer in tact. The current versions of all major non-WebKit browsers do not require a vendor prefix. transition and transitionend are enough. See: caniuse.com/#search=transitions
  • depoulo
    depoulo almost 11 years
    funny enough, I came here 'cause my colleagues just discovered multiple events were thrown in their code which looked exactly like this answer
  • sebastian
    sebastian almost 11 years
    Webkit in version 534 has problem with this. For images function return transitionend but in the end webkitTransitionEnd is fired.
  • Ninjakannon
    Ninjakannon over 10 years
    Being the simplest solution here, it's a real shame this has such a caveat.
  • Dan
    Dan almost 10 years
    @Duopixel please test your answer and consider changing it, because it throws two events in Chrome and Safari (and at least all other Webkit browsers plus old firefox and opera). msTransitionend is not needed here.
  • Dan
    Dan almost 10 years
    jQuery version fires two events in Webkit-based browsers (at least).
  • Tom
    Tom almost 10 years
    @Dan I use one instead of on so it will only fire once
  • Dan
    Dan almost 10 years
    Sorry, I didn't notice you have one instead of on. It was so obvious!
  • Johannes Ewald
    Johannes Ewald over 9 years
    It's worth mentioning that this check doesn't work in some Android 4 browsers which falsely report support for the unprefixed version. See github.com/Modernizr/Modernizr/issues/897 . This can easily be fixed by defining the prefixed version before the unprefixed in the transition-object. While objects officially don't maintain an order, WebKit browsers do.
  • Atav32
    Atav32 over 9 years
    Why does it need to redefine undefined?
  • Tom
    Tom over 9 years
    It's added in their code here github.com/twbs/bootstrap/blob/…
  • Qtax
    Qtax about 9 years
    @Atav32, I wonder that too. The only thing I can think of is that it's there in case someone else redefined it to something already.
  • Nick Budden
    Nick Budden about 9 years
    It will trigger multiple events if you have more than one property transitioned. See: stackoverflow.com/a/18689069/740836
  • Luuuud
    Luuuud about 9 years
    Lovely solution. Unfortunately it won't tell you if transitionend isn't supported at all: var transEndEventName = ('WebkitTransition' in document.documentElement.style) ? 'webkitTransitionEnd' : ('transitionend' in document.documentElement.style) ? 'transitionend' : false; And then do a simple check: if(transEndEventName) element.addEventlistener(transEndEventName, theFunctionToInvoke)
  • Salman von Abbas
    Salman von Abbas about 9 years
    I think that should be checked separately: stackoverflow.com/a/29591030/362006
  • Jessica
    Jessica over 8 years
    Does this answer apply to now as well? (Jan 2016)
  • Jessica
    Jessica over 8 years
    Just tested it in IE 11 and it returned false