Detecting CSS Animation Completion with JavaScript
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 CSS 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 = { 'transition':'transitionend', 'OTransition':'oTransitionEnd', 'MozTransition':'transitionend', 'WebkitTransition':'webkitTransitionEnd' } 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!
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:
http://icanmakethiswork.blogspot.co.uk/2013/12/simple-fading-in-and-out-using-css-transitions.html
I was wondering if you had any better ideas about how to achieve this?
Hi John, take a look how I did it in better-dom – https://github.com/chemerisuk/better-dom/blob/master/src/element.visibility.js. In short I use
visibility:hidden
+position:absolute
to hide and remove element from the flow.Demo: http://jsfiddle.net/C3WeM/5/ (a bit outdated but the latest syntax is the same)
I actually dealt with this issue in a post:
http://www.impressivewebs.com/animate-display-block-none/
By solution is a bit hacky, but Christian Heilmann also dealt with something similar and I think his solution is good:
http://christianheilmann.com/2013/09/19/quicky-fading-in-a-newly-created-element-using-css/
i use this:
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.
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:
I just use all of the event name options… What’s wrong with that, other than filling up unnecessary object properties?
JavaScript does provide us the ability to trigger functionality at the end of <> animations and transitions.
Change to <>?
Sorry. Another try.
JavaScript does provide us the ability to trigger functionality at the end of **JavaScript”” animations and transitions.
Change to **CSS**?
Thanks! I used it in my plugin: https://github.com/jacoborus/nanobar
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
Hm, that Gist link did not work: https://gist.github.com/depoulo/9dc8f59d791ec8f8e40c
Thanks Maksim and Louis – will take a look!
That one neither. Next try: https://gist.github.com/depoulo/9dc8f59d791ec8f8e40c
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
andwebkitAnimationEnd.
respectively. I suspect it’s the same fortransitionend
?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.)
I’am using this jquery-snipe:
I love this method. Yes, it uses jQuery, but looks so much more elegant… Thanks!
This is bad, because it actually fires your callback twice in some circumstances. Once for transitionend, and once for the prefixed version. Example, alerts twice:
What is the importance of checking for the property name
WebkitTransition
instead ofwebkitTransition
? I see they both exist, and point to the same string (===
reportstrue
). However, why not just usewebkitTransition
as the property name?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-11354026I 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 asetTimeout
function do the same stuffI’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?
In my gist above it returns only the first supported event, due to the
$.one()
event listener, which clears itself after the first call.Carefull. It isn’t fired if the Element’s
display
becomesnone
.Btw, try to wrap the event in a Promise (I called mine
addTransitionClass
andremoveTransitionClass
). It’ s fantastic!Be carefull with this. Unfortunately, the event isn’t fired if the browser’s tab isn’t active.
For example:
1) launch your page in tab A and switch immediately on the tab B.
2)
transitionend
event is fired when you switch back to tab A not when the transition has ended.Please confirm that
should read
.