How do I re-trigger a WebKit CSS animation via JavaScript?

David Murdoch picture David Murdoch · Jan 25, 2011 · Viewed 74.4k times · Source

So, I've got this -webkit-animation rule:

@-webkit-keyframes shake {
    0% {
        left: 0;
    }
    25% {
        left: 12px;
    }
    50% {
        left: 0;
    }
    75% {
        left: -12px;
    }
    100% {
        left:0;
    }
}

And some CSS defining some of the animation rules on my box:

#box{
    -webkit-animation-duration: .02s;
    -webkit-animation-iteration-count: 10;
    -webkit-animation-timing-function: linear;
}

I can shake the #box like this:

document.getElementById("box").style.webkitAnimationName = "shake";

But I can't shake it again later.

This only shakes the box once:

someElem.onclick = function(){
    document.getElementById("box").style.webkitAnimationName = "shake";
}

How can I re-trigger a CSS animation via JavaScript without using timeouts or multiple animations?

Answer

David Murdoch picture David Murdoch · Jan 27, 2011

I found the answer based on the source code and examples at the CSS3 transition tests github page.

Basically, CSS animations have an animationEnd event that is fired when the animation completes.

For webkit browsers this event is named “webkitAnimationEnd”. So, in order to reset an animation after it has been called you need to add an event-listener to the element for the animationEnd event.

In plain vanilla javascript:

var element = document.getElementById('box');

element.addEventListener('webkitAnimationEnd', function(){
    this.style.webkitAnimationName = '';
}, false);

document.getElementById('button').onclick = function(){
    element.style.webkitAnimationName = 'shake';
    // you'll probably want to preventDefault here.
};

and with jQuery:

var $element = $('#box').bind('webkitAnimationEnd', function(){
    this.style.webkitAnimationName = '';
});

$('#button').click(function(){
    $element.css('webkitAnimationName', 'shake');
    // you'll probably want to preventDefault here.
});

The source code for CSS3 transition tests (mentioned above) has the following support object which may be helpful for cross-browser CSS transitions, transforms, and animations.

Here is the support code (re-formatted):

var css3AnimationSupport = (function(){
    var div = document.createElement('div'),
        divStyle = div.style,
        // you'll probably be better off using a `switch` instead of theses ternary ops
        support = {
            transition:
                divStyle.MozTransition     === ''? {name: 'MozTransition'   , end: 'transitionend'} :
                // Will ms add a prefix to the transitionend event?
                (divStyle.MsTransition     === ''? {name: 'MsTransition'    , end: 'msTransitionend'} :
                (divStyle.WebkitTransition === ''? {name: 'WebkitTransition', end: 'webkitTransitionEnd'} :
                (divStyle.OTransition      === ''? {name: 'OTransition'     , end: 'oTransitionEnd'} :
                (divStyle.transition       === ''? {name: 'transition'      , end: 'transitionend'} :
                false)))),
            transform:
                divStyle.MozTransform     === '' ? 'MozTransform'    :
                (divStyle.MsTransform     === '' ? 'MsTransform'     :
                (divStyle.WebkitTransform === '' ? 'WebkitTransform' : 
                (divStyle.OTransform      === '' ? 'OTransform'      :
                (divStyle.transform       === '' ? 'transform'       :
                false))))
            //, animation: ...
        };
    support.transformProp = support.transform.name.replace(/([A-Z])/g, '-$1').toLowerCase();
    return support;
}());

I have not added the code to detect “animation” properties for each browser. I’ve made this answer “community wiki” and leave that to you. :-)