How to use jQuery to wait for the end of CSS3 transitions?
Solution 1
For transitions you can use the following to detect the end of a transition via jQuery:
$("#someSelector").bind("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", function(){ ... });
Mozilla has an excellent reference:
For animations it's very similar:
$("#someSelector").bind("animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd", function(){ ... });
Note that you can pass all of the browser prefixed event strings into the bind() method simultaneously to support the event firing on all browsers that support it.
Update:
Per the comment left by Duck: you use jQuery's .one()
method to ensure the handler only fires once. For example:
$("#someSelector").one("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", function(){ ... });
$("#someSelector").one("animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd", function(){ ... });
Update 2:
jQuery bind()
method has been deprecated, and on()
method is preferred as of jQuery 1.7
. bind()
You can also use off()
method on the callback function to ensure it will be fired only once. Here is an example which is equivalent to using one()
method:
$("#someSelector")
.on("animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd",
function(e){
// do something here
$(this).off(e);
});
References:
Solution 2
There is an animationend
Event that can be observed see documentation here,
also for css transition
animations you could use the transitionend
event
There is no need for additional libraries these all work with vanilla JS
document.getElementById("myDIV").addEventListener("transitionend", myEndFunction);
function myEndFunction() {
this.innerHTML = "transition event ended";
}
#myDIV {transition: top 2s; position: relative; top: 0;}
div {background: #ede;cursor: pointer;padding: 20px;}
<div id="myDIV" onclick="this.style.top = '55px';">Click me to start animation.</div>
Solution 3
Another option would be to use the jQuery Transit Framework to handle your CSS3 transitions. The transitions/effects perform well on mobile devices and you don't have to add a single line of messy CSS3 transitions in your CSS file in order to do the animation effects.
Here is an example that will transition an element's opacity to 0 when you click on it and will be removed once the transition is complete:
$("#element").click( function () {
$('#element').transition({ opacity: 0 }, function () { $(this).remove(); });
});
Solution 4
For anyone that this might be handy for, here is a jQuery dependent function I had success with for applying a CSS animation via a CSS class, then getting a callback from afterwards. It may not work perfectly since I had it being used in a Backbone.js App, but maybe useful.
var cssAnimate = function(cssClass, callback) {
var self = this;
// Checks if correct animation has ended
var setAnimationListener = function() {
self.one(
"webkitAnimationEnd oanimationend msAnimationEnd animationend",
function(e) {
if(
e.originalEvent.animationName == cssClass &&
e.target === e.currentTarget
) {
callback();
} else {
setAnimationListener();
}
}
);
}
self.addClass(cssClass);
setAnimationListener();
}
I used it kinda like this
cssAnimate.call($("#something"), "fadeIn", function() {
console.log("Animation is complete");
// Remove animation class name?
});
Original idea from http://mikefowler.me/2013/11/18/page-transitions-in-backbone/
And this seems handy: http://api.jqueryui.com/addClass/
Update
After struggling with the above code and other options, I would suggest being very cautious with any listening for CSS animation ends. With multiple animations going on, this can get messy very fast for event listening. I would strongly suggest an animation library like GSAP for every animation, even the small ones.
Solution 5
The accepted answer currently fires twice for animations in Chrome. Presumably this is because it recognizes webkitAnimationEnd
as well as animationEnd
. The following will definitely only fires once:
/* From Modernizr */
function whichTransitionEvent(){
var el = document.createElement('fakeelement');
var transitions = {
'animation':'animationend',
'OAnimation':'oAnimationEnd',
'MSAnimation':'MSAnimationEnd',
'WebkitAnimation':'webkitAnimationEnd'
};
for(var t in transitions){
if( transitions.hasOwnProperty(t) && el.style[t] !== undefined ){
return transitions[t];
}
}
}
$("#elementToListenTo")
.on(whichTransitionEvent(),
function(e){
console.log('Transition complete! This is the callback!');
$(this).off(e);
});
Related videos on Youtube
C.J.
Updated on January 07, 2022Comments
-
C.J. over 2 years
I'd like to fade out an element (transitioning its opacity to 0) and then when finished remove the element from the DOM.
In jQuery this is straight forward since you can specify the "Remove" to happen after an animation completes. But if I wish to animate using CSS3 transitions is there anyway to know when the transition/animation has completed?
-
Nicolas Modrzyk over 12 yearsshould be similar to this: stackoverflow.com/questions/6186454/…
-
Varun Singh almost 12 years
-
-
Jonathan Liuti over 10 yearsThe callback is not working - maybe it's due to a change in transit's api I don't know, but the fiddle example is not working. It triggers the hide method before the animation runs (tried in chrome)
-
Gaff over 10 years@JonathanLiuti tested using FireFox 25, IE11, Chrome 31. Works fine.
-
Jonathan Liuti over 10 yearsYea @ROFLwTIME you are totally right - it seems my chrome was just going crazy. Retested the fiddle today after a clean restart of chrome and it's working as expected. My bad ! Sorry 'bout this one.
-
SikoSoft over 10 yearsIt's worth noting that the callback will be fired for every child element that transits as well. This is very important to keep in mind in case you are wondering why your callback gets fired more times than you were expecting. I am not aware of any work-around as of now.
-
Jim Jeffers over 10 years@Lev you could easily mitigate this by comparing the event's currentTarget to its target. So something like: function(event){ if(event.target === event.currentTarget){ /* Do stuff */ } }
-
SikoSoft over 10 yearsYeah, I figured that out shortly after I wrote the comment. >_< Thanks for posting it though; I'm sure it will help others! :)
-
olo almost 10 years
-
kroe almost 10 years@JimJeffers you can use the "return home early" pattern, by doing, if( event.target != event.currentTarget ) return;
-
ditto over 9 yearsUse
.one()
instead of.on()
or.bind()
. This will ensure that it may only run once, and afterwards it'll unbind itself from the event handler. -
DMSJax over 8 yearsBecause I ran into a similar problem, i found the answer elsewhere however this SO answer was in my open tabs (just found answer on another tab first) anyways - here is a simple jsfiddle example of @AJFarmer use of .one() on a CSS
animation
transition Jsfiddle -
WebDevNerdStuff over 8 yearsAll modern browsers now support the unprefixed event. caniuse.com/#feat=css-transitions Also note that if you have "transitionend webkitTransitionEnd" it will be triggered twice in Chrome.
-
Xharlie about 8 yearsI prefer
one()
because you don't have to construct another jQuery object with$(this)
just to remove the binding in the event handler. It just seems cleaner. -
Xharlie about 8 yearsSince jQuery is in the picture, anyway, I have a simple and robust solution to ensure that a callback only gets fired once: the code that begins the transition returns a promise that is resolved by the
animationend
handler. The code that is to run after the animation ends is simply queued on the promise withthen()
-
Jason Kleban almost 8 yearsHere's an example fiddle of this technique that demonstrates animating the attach/removal of an element without any timeouts. It is in typescript can be converted to javascript by pasting into the typescript playground. No jQuery either.
-
Chuck Le Butt almost 8 yearsThis still fires twice in Chrome. Presumably because it supports webkitAnimationEnd as well as animationEnd.
-
Jakob Løkke Madsen over 7 yearsI would suggest to call the function whichAnimationEvent() instead, since it deals with animation events.
-
logidelic over 7 yearsThis is great, but I wonder if there is a way to trigger this event immediately if there is no transition in progress. Imagine the case where some transition:xxx gets removed from CSS for aesthetic reasons, and then it inexplicably breaks code that was expecting this transitionend event upon changing of some style's value...
-
BomAle almost 6 yearsThanks for share it, I have used it and edited adding
e.stopImmediatePropagation(); self.trigger(this.whichAnimationEvent()); //for purge existing event callback.apply(self);
-
Blue over 5 yearsThis is a borderline link-only answer. You should expand your answer to include as much information here, and use the link only for reference.
-
Aaron Mason over 5 yearsVoting this one up as it's the first one that doesn't rely on jQuery. No sense cutting down an entire tree for a branch.
-
A Haworth over 3 yearsNow browsers are more in step - for example, not needing prefixes - this vanilla Javascript solution would probably be the accepted answer nowadays (2020). Upvoted for not relying on jQuery to answer a relatively straightforward question.