How do I normalize the CSS3 transition end events across browsers?
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.
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);
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, 2022Comments
-
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 about 13 yearsThanks! 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 about 13 yearsI agree, I've been meaning to replace mine with something better, but on the other hand I like the simplicity of it.
-
Matijs about 13 yearsFor what it's worth. This can be done without browser sniffing by just doing
object.bind('transitionend oTransitionEnd webkitTransitionEnd', function() { // callback } );
-
vieron over 11 yearsoTransitionEnd was lowercased to otransitionend in Opera. See opera.com/docs/specs/presto2.10/#m274
-
gossi over 11 yearsit also is transitionend in all lowercase now. See dev.w3.org/csswg/css3-transitions/#transition-events
-
wwaawaw over 11 yearsThat second-to-last one shouldn't be camelCased.
-
alt over 11 yearsYeah, to get this to work in Firefox now
'transition':'transitionEnd'
needs to be'transition':'transitionend'
-
alt over 11 yearsThis is a great way to do it but it requires Modernizr. Can this be written simply but without Modernizr?
-
redochka over 11 yearsAlso 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 over 11 yearsFor some reason this code failed in FF for me. Solved the issue thanks to this page marakana.com/static/bookshelf/css3_transitions_tutorial/…
-
mgol over 11 yearsThe non-prefixed version of the event is named
transitionend
, notTransitionEnd
. -
webinista over 11 yearsI 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
andtransitionend
are enough. See: caniuse.com/#search=transitions -
depoulo almost 11 yearsfunny enough, I came here 'cause my colleagues just discovered multiple events were thrown in their code which looked exactly like this answer
-
sebastian almost 11 yearsWebkit in version 534 has problem with this. For images function return transitionend but in the end webkitTransitionEnd is fired.
-
Ninjakannon over 10 yearsBeing the simplest solution here, it's a real shame this has such a caveat.
-
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 almost 10 yearsjQuery version fires two events in Webkit-based browsers (at least).
-
Tom almost 10 years@Dan I use one instead of on so it will only fire once
-
Dan almost 10 yearsSorry, I didn't notice you have
one
instead ofon
. It was so obvious! -
Johannes Ewald over 9 yearsIt'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 over 9 yearsWhy does it need to redefine
undefined
? -
Tom over 9 yearsIt's added in their code here github.com/twbs/bootstrap/blob/…
-
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 about 9 yearsIt will trigger multiple events if you have more than one property transitioned. See: stackoverflow.com/a/18689069/740836
-
Luuuud about 9 yearsLovely 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 about 9 yearsI think that should be checked separately: stackoverflow.com/a/29591030/362006
-
Jessica over 8 yearsDoes this answer apply to now as well? (Jan 2016)
-
Jessica over 8 yearsJust tested it in IE 11 and it returned false