3

The .animate() function in jQuery does not allow all CSS3 animatable properties to be animated (for example, background-color). Is there a nice, standard way to dynamically create, apply, and remove CSS3 animations to elements on the page?

I'm currently following the example here but this is clunky and feels wrong. While it works, I would rather a better solution (using a library or something like that).

4
  • Take a look at bounce.js Commented Feb 3, 2016 at 0:52
  • That doesn't make any mention of background-color or other things jquery can't already do. Am I missing something or is bounce.js a different implementation of jquery.animate()? Commented Feb 3, 2016 at 0:58
  • Can you just use addClass and removeClass and set your animations in your css? Commented Feb 3, 2016 at 2:26
  • The animations themselves need to be dynamic Commented Feb 3, 2016 at 5:56

1 Answer 1

2

Yes, we can dynamically create, apply and remove CSS3 animations to an element in the page.

To dynamically create an animation, we need to use the insertRule or addRule functions to add the @keyframes rules and append it to the stylesheet. Once the animation is appended, applying it to the element is very simple, we just need to set the required property value to the animation property via inline styles. Removing it agian is very simple, we just need to set the value back to null when it needs to be removed.

In the below snippet, I have first inserted an animation and applied it to the element on load. When the animation starts, the element fires the animationstart event. Within this event listener, I've obtained the outerHTML of the element that is being animated and printed it to show how inline style is present and then at the end of the animation (using the animationend event listener), I've removed the inline style and printed the outerHTML after it to show how it no longer has the animation.

There are no other simpler ways to dynamically create CSS3 animations. All possible solutions will involve creating and appending @keyframes to the stylesheets using basic insertRule, addRule or the keyframes specific appendRule function (which is used to append rules to an existing keyframe).

var elm = document.querySelector('.to-animate');
var op = document.querySelector('.output');

var animName = "shake-up-down",
  animDuration = "3s",
  animTiming = "linear",
  animFillMode = "forwards",
  animIteration = "3";

var ruleText = "0% {transform: translateY(0px);}";
ruleText += "25% {transform: translateY(10px);}";
ruleText += "75% {transform: translateY(-10px);}";
ruleText += "100% {transform: translateY(0px);}";


/* From David Walsh's blog */
function addCSSAnimRule(sheet, name, rules, index) {
  if ("insertRule" in sheet) {
    sheet.insertRule("@keyframes " + name + "{" + rules + "}", index);
  } else if ("addRule" in sheet) {
    sheet.addRule("@keyframes " + name, rules, index);
  }
}

/* Self written */
function applyAnimation(elm, name, duration, timing, iteration, fillmode) {
  elm.style["animation"] = name + " " + duration + " " + timing + " " + iteration + " " + fillmode;
  
  /* or if you want to set them individually, comment the above line and uncomment this
  elm.style["animationName"] = name;
  elm.style["animationDuration"] = duration;
  elm.style["animationTimingFunction"] = timing;
  elm.style["animationIterationCount"] = iteration
  elm.style["animationFillMode"] = fillmode;*/
}

addCSSAnimRule(document.styleSheets[0], animName, ruleText, 0);
applyAnimation(elm, animName, animDuration, animTiming, animIteration, animFillMode);

/* to print output */

elm.addEventListener("animationstart", function(e) {
  op.textContent = "Animation " + e.animationName + " has started.";
  op.textContent += "\n\nElement's Outer HTML: \n";
  op.textContent += elm.outerHTML;
  op.textContent += "\n\n------------------------------------------------------------------------------";
});

elm.addEventListener("animationend", function(e) {
  elm.removeAttribute("style"); /* remove the animation */
  op.textContent += "\nAnimation " + e.animationName + " has ended.";
  op.textContent += "\n\nElement's Outer HTML: \n";
  op.textContent += elm.outerHTML;
  op.textContent += "\n\n------------------------------------------------------------------------------";
});
.to-animate {
  height: 100px;
  width: 100px;
  margin: 10px;
  border: 1px solid red;
}
<div class='to-animate'></div>
<pre class='output'></pre>


This method can be used to dynamically create and use any type of animation. Below snippet creates and adds a background-color animation.

var elm = document.querySelector('.to-animate');
var op = document.querySelector('.output');

var animName = "bgColor",
  animDuration = "4s",
  animTiming = "linear",
  animFillMode = "forwards",
  animIteration = "3";

var ruleText = "0% {background-color: red;}";
ruleText += "25% {background-color: orange;}";
ruleText += "50% {background-color: yellow;}";
ruleText += "75% {background-color: pink;}";
ruleText += "100% {background-color: red;}";


/* From David Walsh's blog */
function addCSSAnimRule(sheet, name, rules, index) {
  if ("insertRule" in sheet) {
    sheet.insertRule("@keyframes " + name + "{" + rules + "}", index);
  } else if ("addRule" in sheet) {
    sheet.addRule("@keyframes " + name, rules, index);
  }
}

/* Self written */
function applyAnimation(elm, name, duration, timing, iteration, fillmode) {
  elm.style["animation"] = name + " " + duration + " " + timing + " " + iteration + " " + fillmode;

  /* or if you want to set them individually, comment the above line and uncomment this
  elm.style["animationName"] = name;
  elm.style["animationDuration"] = duration;
  elm.style["animationTimingFunction"] = timing;
  elm.style["animationIterationCount"] = iteration
  elm.style["animationFillMode"] = fillmode;*/
}

addCSSAnimRule(document.styleSheets[0], animName, ruleText, 0);
applyAnimation(elm, animName, animDuration, animTiming, animIteration, animFillMode);

/* to print output */

elm.addEventListener("animationstart", function(e) {
  op.textContent = "Animation " + e.animationName + " has started.";
  op.textContent += "\n\nElement's Outer HTML: \n";
  op.textContent += elm.outerHTML;
  op.textContent += "\n\n------------------------------------------------------------------------------";
});

elm.addEventListener("animationend", function(e) {
  elm.removeAttribute("style"); /* remove the animation */
  op.textContent += "\nAnimation " + e.animationName + " has ended.";
  op.textContent += "\n\nElement's Outer HTML: \n";
  op.textContent += elm.outerHTML;
  op.textContent += "\n\n------------------------------------------------------------------------------";
});
.to-animate {
  height: 100px;
  width: 100px;
  margin: 10px;
  background-color: red;
}
<div class='to-animate'></div>
<pre class='output'></pre>


Cross Browser Version:

Here is a cross browser version with support for older browsers using methods exposed by the Prefix free library. This was tested in IE10+, Edge, Chrome v50 (dev-m), Firefox v43, Opera v35. Testing for prefixed version was done in Safari 5.1.7 on Win 10.

Sign up to request clarification or add additional context in comments.

5 Comments

I like this one. +1 The only thing missing is cross-browser support. (Or at least vendor prefixes for CSS3 capable browsers.) You can use another snippet by David for this: detect vendor prefix.
@BramVanroy: Thank you very much. I was actually torn between whether to use David's script (or) use a library like prefix-free and let them find out the prefix required for the browser. I think I'll go down the second route to avoid bloating up the code more (but your comment is definitely valuable).
I didn't know PrefixFree was capable of adding vendor prefixes after DOM change/style rule insertion but I looked it up and you are right indeed! (May require its plugin to work properly.)
Yep @BramVanroy and it also exposes a variable (PrefiFree.prefix) which we can use to get the prefix applicable for the user's browser.
@BramVanroy: This is what I was talking about. Using the Prefix free's exposed functions and variables. Tested and works in all browsers (prefix testing was done in an older version of Safari).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.