Callback on CSS transition
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 );
CSS transition doesn't start/callback isn't called
For a comprehensive explanation of why this happens, see this Q/A, and this one.
Basically, at the time you set the new style
the browser still has not applied the one set inline, your element's computed style still has its display
value set to ""
, because it's what elements that are not in the DOM default to.
Its left
and top
computed values are still 0px
, even though you did set it in the markup.
This means that when the transition
property will get applied before next frame paint, left
and top
will already be the ones you did set, and thus the transition will have nothing to do: it will not fire.
To circumvent it, you can force the browser to perform this recalc. Indeed a few DOM methods need the styles to be up to date, and thus browsers will be forced to trigger what is also called a reflow.Element.offsetHeight
getter is one of these method:
let tablehtml = `<div id="spanky" style="position: absolute; left: 10px; top: 10px; background-color:blue; width:20px; height:20px; transition: left 1000ms linear 0s, top 1000ms linear 0s;"></div>`;
document.body.innerHTML += tablehtml;
let animdiv = document.getElementById('spanky');animdiv.addEventListener("transitionend", function(event) { animdiv.style.backgroundColor='red';}, false);// force a reflowanimdiv.offsetTop;// now animdiv will have all the inline styles set// it will even have a proper display
animdiv.style.backgroundColor='green';Object.assign(animdiv.style, { left: "100px", top: "100px" });
Is there a callback on completion of a CSS3 animation?
Yes, there is. The callback is an event, so you must add an event listener to catch it. This is an example with jQuery:
$("#sun").bind('oanimationend animationend webkitAnimationEnd', function() {
alert("fin")
});
Or pure js:
element.addEventListener("webkitAnimationEnd", callfunction,false);
element.addEventListener("animationend", callfunction,false);
element.addEventListener("oanimationend", callfunction,false);
Live demo:
http://jsfiddle.net/W3y7h/
Callback on CSS animation end
$('#my_object').animate_scale().fadeOut(2000);
if you want .fadeOut()
to wait for animate_scale()
to finish, animate_scale
needs to be queued:
Queue your plugin:
Usually, when you chain fx methods like i.e:
$("#ball").animate({left:200}).fadeOut();
you'll see the ball animate, and only once that animation is finished --- it'll fade out.
Why? Cause jQuery will stach animate
and than fadeOut
into a queue
array and wait each to resolve before triggering the next Method.
To replicate the same behavior within your plugin:
jsFiddle demo (Queue in action!)
$.fn.animate_scale = function( callback ) {
var $this = this;
return $this.queue(function() {
$this.addClass('animate_scale').on("animationend", function() {
$this.dequeue();
if (typeof callback == 'function') callback.call( $this );
});
});
};
$('#my_object').animate_scale(function() {
console.log( "Scale is done!" );
}).fadeOut( 2000 ); // fadeOut will wait for animate_scale to dequeue (complete)
I don't need queue stacking
If you want your plugin to unobstructively (simultaneously) process other chained fx Methods,
use just the callback:
jsFiddle demo (no Queue)
$.fn.animate_scale = function( callback ) {
var $this = $(this);
return $this.addClass('animate_scale').on("animationend", function() {
if (typeof callback == 'function') callback.call( this );
});
};
$('#my_object').animate_scale(function(){
console.log("Scale done.");
// use $(this).fadeOut(2000); here!! cause otherwise...
}).fadeOut(2000); // ...if chained here, will fade immediately!!!!!
How to use jQuery to wait for the end of CSS3 transitions?
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:
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions#Detecting_the_start_and_completion_of_a_transition
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:
.off()
.one()
CSS Transition fails on jQuery .load callback
When element is added, reflow is needed. The same applies to adding the class. However when you do both in single javascript round, browser takes its chance to optimize out the first one. In that case, there is only single (initial and final at the same time) style value, so no transition is going to happen.
The setTimeout trick works, because it delays the class addition to another javascript round, so there are two values present to the rendering engine, that needs to be calculated, as there is point in time, when the first one is presented to the user.
There is another exception of the batching rule. Browser need to calculate the immediate value, if you are trying to access it. One of these values is offsetWidth. When you are accessing it, the reflow is triggered. Another one is done separately during the actual display. Again, we have two different style values, so we can interpolate them in time.
This is really one of very few occasion, when this behaviour is desirable. Most of the time accessing the reflow-causing properties in between DOM modifications can cause serious slowdown.
The preferred solution may vary from person to person, but for me, the access of offsetWidth (or getComputedStyle()) is the best. There are cases, when setTimeout is fired without styles recalculation in between. This is rare case, mostly on loaded sites, but it happens. Then you won't get your animation. By accessing any calculated style, you are forcing the browser to actually calculate it
Trigger CSS transition on appended element
Explanation For the last part
The .css() method is a convenient way to get a style property from the first matched element, especially in light of the different ways browsers access most of those properties (the getComputedStyle() method in standards-based browsers versus the currentStyle and runtimeStyle properties in Internet Explorer) and the different terms browsers use for certain properties.
In a way .css() is jquery equivalent of javascript function getComputedStyle()
which explains why adding the css property before adding class made everything work
Jquery .css() documentation
// Does not animatevar $a = $('<div>') .addClass('box a') .appendTo('#wrapper'); $a.css('opacity'); $a.addClass('in');
// Check it's not just jQuery// does not animatevar e = document.createElement('div');e.className = 'box e';document.getElementById('wrapper').appendChild(e);window.getComputedStyle(e).opacity;e.className += ' in';
.box { opacity: 0; -webkit-transition: all 2s; -moz-transition: all 2s; transition: all 2s; background-color: red; height: 100px; width: 100px; margin: 10px;}
.box.in { opacity: 1;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script><div id="wrapper"></div>
Where to put callback function in react-transition-group
You can use addEndListener
, or even onExited
and onEntered
callbacks.
With addEndListener
:
function abc() {
// blah
// blah
//blah
}
... // some code
<Transition
addEndListener={(node, done) => {node.addEventListener('transitionend',(e) => {
abc(e);
done(e);
}, false);}}>
<MyComponent />
</Transition>
With onEntered
and onExited
:
<Transition
onEntered={abc} onExited={abc}>
<MyComponent />
</Transition>
An important thing of the second example: check the moment, when you want to get your callback called.
How do I detect a transition end without a JavaScript library?
element.addEventListener('transitionend', function(event) {
alert("CSS Property completed: " + event.propertyName);
}, false );
For now, the exact event name has not been standardized. Here's a quote from MDN:
There is a single event that is fired when transitions complete.
In all standard-compliant browser, the event istransitionend
,
in WebKit it iswebkitTransitionEnd
.
Here's the fiddle for Webkit: http://jsfiddle.net/bNgWY/
Related Topics
Getelementbyid() Returns Null Even Though the Element Exists
What's Standard Behavior When ≪Button≫ Element Is Clicked? Will It Submit the Form
Html 5 Canvas Lineto() Line Color Issues
JavaScript Trick For 'Paste as Plain Text' in Execcommand
Adding Additional Data to Select Options Using Jquery
Dynamically Loading CSS Stylesheet Doesn't Work on Ie
Jquery Event Won't Fire After Ajax Call
How to Detect "Shift+Enter" and Generate a New Line in Textarea
Relative Paths in JavaScript in an External File
JavaScript Dom: Find Element Index in Container
Programmatically Trigger "Select File" Dialog Box
How to Detect When a Page Exits Fullscreen
JavaScript - Shuffle HTML List Element Order