With CSS animations (ala @keyframes) it’s not as easy as you might think to “restart” it.
Let’s say you set it to run once:
.run-animation {
animation: my-fancy-animation 5s 1;
}
You have that run on, say, your logo:
<h1 id="logo" class="run-animation">
Fancy Logo
</h1>
Then for some reason you want to have that animation run again, perhaps on a click. You might think CSS provides a way to kick it off again, but as far as I know it doesn’t. You might even try removing and re-adding that class name via JavaScript (jQuery):
$("#logo").click(function() {
// not gonna work
$(this).removeClass("run-animation").addClass("run-animation");
});
If you put a tiny delay between removing and re-adding the class (e.g. setTimeout()
), it will work, but I think it goes without saying that that isn’t ideal. What you really need to do is remove the element from the page entirely and re-insert it. As a quick demo with jQuery, we’ll clone it, insert it right after itself, then remove the original.
$("#logo").click(function() {
var el = $(this),
newone = el.clone(true);
el.before(newone);
$("." + el.attr("class") + ":last").remove();
});
or the basics with just JavaScript:
const el = this;
var newone = elm.cloneNode(true);
elm.parentNode.replaceChild(newone, elm);
This same problem exists for any type of event. Say you have an animation you want to run immediately, but then again on :hover
. Same problem, it won’t run again. Oli Studholme has some interesting research on this. One way around it is by having two animations that are exactly the same except for their name, then you can switch which one you use for the :hover
event (or whatever) and it will work.
img {animation: spin-cat-1 2s;}
img:active {animation: spin-cat-2 2s;}
@keyframes spin-cat-1 {50%{transform:rotateY(180deg);}}
@keyframes spin-cat-2 {50%{transform:rotateY(180deg);}}
A CSS preprocessor could help make maintaining that easier by importing a chunk of code so you only have to maintain it in one place, but ultimately it’s bloat-y. Unfortunately, you also cannot define multiple keyframe animation names in one declaration, which would also be useful (and not just for this, think of more complex animations where you could rewrite just certain keyframes over a base keyframe animation).
Oli also played with a variety of other techniques to get it to work, like altering duration, delays, and iterations instead of changing the name, but in my opinion, all of those are even worse than changing the name.
Update: Another JavaScript Method to Restart a CSS Animation
Trigger a reflow in between removing and adding the class name. For example:
// retrieve the element
element = document.getElementById("logo");
// reset the transition by...
element.addEventListener("click", function(e) {
e.preventDefault;
// -> removing the class
element.classList.remove("run-animation");
// -> triggering reflow /* The actual magic */
// without this it wouldn't work. Try uncommenting the line and the transition won't be retriggered.
// Oops! This won't work in strict mode. Thanks Felis Phasma!
// element.offsetWidth = element.offsetWidth;
// Do this instead:
void element.offsetWidth;
// -> and re-adding the class
element.classList.add("run-animation");
}, false);
Other JavaScript
Just for the record, you can also affect the playing state of a CSS animation through JavaScript, like pausing and restarting it.
element.style.animationPlayState="paused";
element.style.animationPlayState="running";
// Vendor prefixed in old browsers
// element.style.webkitAnimationPlayState="paused";
// element.style.webkitAnimationPlayState="running";
Interestingly, the natural end of a CSS animation doesn’t set the play state to “paused”. You would have to do that yourself. Say you wanted to restart an animation for every hover and you were cool using jQuery to do it…
$("#thing").hover(function() {
this.style.animationPlayState = "running";
$(this).on('animationEnd', function() {
this.style.animationPlayState = "paused";
});
});
Notice how you have to watch for the AnimationEnd
event to fire to set it back to “paused” or it won’t restart properly.
What are you doing on the last line of code? Are you just trying to remove the element? Couldn’t it be done through el.remove()?
You now have two elements with the same class name so using ‘el.remove’ will remove them both so you need specify that it needs to remove the last one.
I think thats correct ha
Can’t be, bc el == $(this). It hasn’t been manipulated from what I can see
Nice post Chris!
Paul, yeah it could be simplified somewhat by doing…
http://jsfiddle.net/t3EaG/
Even more simplified:
$(this).replaceWith($(this).clone());
$(this).replaceWith($(this).clone(true)); is what I meant
I had a thought about using insertBefore on the element to reinsert it but that didn’t work. One thing though, the demo has the animation on the h1 element not the class, so removing and replacing the class can’t work…
Hmm… a JS free way would be to do it with transitions, rather than animations as those work on hover and active, but that obviously limits you:
It’s a bodge for this simple animation, anyway ;)
To restart the animation, wrap the addClass method with a setTimeout() should do the trick. Something similar to this event handler:
This is also what I think the issue is. Removing and adding a class name in the same execution cycle (your event handler in this instance) will result in nothing changing in the DOM once the JavaScript has yielded.
The setTimeout will result in the JavaScript yielding between removing the class name and re-adding it, which allows the browser to notice the DOM manipulation and act upon it.
You don’t really need to wrap the addClass method, you can just create a closure over $(this) within your onclick handler and reference it in your setTimeout call (like the elem variable in clamster’s comment), so you can still use jQuery removeClass and addClass if $(this) is assigned to elem.
Another way of doing it using addClass/removeClass is to force a reflow between the calls, which is a bit nicer than setTimeout imho:
Editor’s note: Jorge Antunes wrote in to say this works great and provided a CodePen demo.
Jesper, I agree it’s nicer, but that’s the approach that the article says is not working, isn’t it?
You can remove the classname as soon as the animation ends (‘webkitAnimationEnd’), and then add it again when you need it on click or whatever event. http://jsfiddle.net/nimbu/vxcYJ/
but if it is an infinite animation?
I am not sure why you would want to restart an infinite animation :/
In any case, you need to remove the webkitAnimation property from the style and then set it again after a delay to get it to start.
Pausing animations is anyway covered by animation-play-state property.
Just wanted to say, if you wish to cancel an infinitely repeating event, you can check for this event ‘webkitAnimationIteration’ which is fired whenever the animation iteration count is more than 1.
This is simple and elegant – bravo.
If you’re going to use jQuery anyway, why not just to the whole animation in jQuery, which will be cross browser compatible?
You certainly could. There are considerations and it depends on real use cases, so it’s hard to debate theoretically. They both can do things each other can’t do. JavaScript based animations have deeper support but CSS animations are generally smoother and can be hardware accelerated.
True that, Chriss, but you’re doing vendor specific animations here and hardware accelerated or not, you still need a fallback for browsers that don’t support the animation using CSS, so you might as well do it in jQuery, unless you’ve go the feature detection way and target non-webkit browsers in this case.
From what I’ve seen jQuery/Javascript animations are smoother then CSS3 ones…
And the debate goes on! This was actually my question also. Why start mixing css animations and javascript animations? Seems like it would be a pain to come back to in the future.
I think it’ll be easier to stick with one and since CSS isn’t really ready yet (browser support, inability to restart animations), just stick with javascript.
Animation was smooth on Safari and Chrome, but not on IE 8. I haven’t tried on Firefox though.
I bellieve that’s because IE8 does not support css animations.
Very useful thanks.
Hello,
I ran into this problem not so long ago, the way I solved it was by removing the class then setting a 0ms timer for re-adding it.
$('something').removeClass('anim').animate({'nothing':null}, 1, function () {
$(this).addClass('anim');
});
Simple, yet effective!
I will translate the Restart CSS Animation to my language Turkish.
The easiest way to replay css animations from the beginning is if elements are placed in a main wrapper:
function Replay(){
var container = document.getElementById(“wrapper”);
var content = container.innerHTML;
container.innerHTML= content;
console.log(“replay”);
}
I ran into an ie11 keyframes animation problem and you have the same problem here, basically none of the animations play :(
I have almost 0 jquery knowledge. What you did above was ACE and perfect only thing is i need it to be timed so e.g. remove the element after 3 seconds and then add it again after 40 seconds. How would i go about doing this?