Four Essential JavaScript functions to tame CSS3 Transitions and Animations
Created: Jan 4th, 17'
CSS3 transitions make it easy to animate between CSS property changes. Its simple CSS syntax make it accessible to everyone, though at the same time limiting on its own for creating complex, event driven animations. In this tutorial we'll go over 4 essential JavaScript routines that help you unlock the full potential of CSS Transitions and Animations.
1) classList
API for adding and removing CSS classes
The classList
API provides a native way to add, remove,
toggle, or check the existence of a CSS class on an element. It's a lot
easier than parsing element.className
:
element.classList.add("someclass") //adds a class to element element.classList.add("class1", "class2", etc) //adds multiple CSS classes. Not supported in IE11 or FF26 or less. element.classList.remove("someclass") //removes a class from element element.classList.remove("class1", "class2", etc) //removes multiple CSS classes. Not supported in IE11 or FF26 or less. element.classList.toggle("someclass") //toggles a class. If already exists removes and returns false. Otherwise adds class and returns true element.classList.toggle("someclass", expression) // adds or removes "someclass" based on outcome of expression. Not supported in IE11 or FF26 or less. element.classList.contains("someclass") // check if the a class is present inside the element
Basic support of classList
is really
good, and if you also need to cater to older IE browsers, there's
a good pollyfill for that.
With the classList
API, we can store CSS transitions to be played in a
CSS class, and play and reverse them on demand by adding or removing them
to an element. it's almost magical:
<style type="text/css"> #abox{ width: 250px; height: 100px; background: navy; margin-bottom: 1em; -webkit-transform: scale(0.5); transform: scale(0.5); opacity: 0; -webkit-transition: all .5s; transition: all .5s; } #abox.openbox{ -webkit-transform: scale(1); transform: scale(1); opacity: 1; } </style> <div id="abox"></div> <button onClick="document.getElementById('abox').classList.toggle('openbox')">Show/ Hide</button>
Demo:
Adding and removing CSS
classes makes light work of applying CSS transitions on demand, with all the
convoluted vendor prefixes for select properties such as "transform
"
defined in the CSS.
2) Dynamically set CSS3 properties' values
Dynamically setting CSS3 property values using JavaScript gives us even
more granular control over our CSS transitions. From the tutorial
Setting CSS3 properties using JavaScript, the following is a generic
CSS3 property setter and getter. It checks in real time which version of
a CSS property in JavaScript form the browser supports (ie: "transform
"
or "WebkitTransform
"), and returns that property.
function getcss3prop(cssprop){ var css3vendors = ['', '-moz-','-webkit-','-o-','-ms-','-khtml-'] var root = document.documentElement function camelCase(str){ return str.replace(/\-([a-z])/gi, function (match, p1){ // p1 references submatch in parentheses return p1.toUpperCase() // convert first letter after "-" to uppercase }) } for (var i=0; i<css3vendors.length; i++){ var css3propcamel = camelCase( css3vendors[i] + cssprop ) if (css3propcamel.substr(0,2) == 'Ms') // if property starts with 'Ms' css3propcamel = 'm' + css3propcamel.substr(1) // Convert 'M' to lowercase if (css3propcamel in root.style) return css3propcamel } return undefined }
Simply enter an unaltered CSS3 property to get the supported version by
the browser back. If the browser doesn't support the property in any
form, undefined
is returned.
var transformprop = getcss3prop('transform') // returns 'transform' or one of the variants, such as 'msTransform', 'MozTransform', 'WebkitTransform' etc var transitionprop = getcss3prop('transition') // returns 'transition' or one of the variants, such as 'MozTransition', 'WebkitTransition' etc
Once you get the supported version of the CSS3 property, you can probe or set its value dynamically, for example:
if (transformprop){ //if defined var el = document.getElementById('box') el.style[transformprop] = 'rotate(180deg) scale(1.05, 1.05)' }
The following uses the
function to set each DIV"s
transition-delay
property so the values are cumulative, leading
to each DIV showing up one after the next:
Demo:
See the Pen Using JavaScript to set CSS3 Properties by georgec (@georgec) on CodePen.
3) Detecting when a transition has ended
We can use JavaScript to also detect when a CSS transition has ended, by
tapping into the "transitionend
" event. The caveat like setting CSS3
properties is that some older versions of Chrome and Safari still rely
on the prefixed version of the event. To account for all the possible
prefixes, we can use the following function to return the supported "transitionend
"
event in the browser:
function gettransitionend(){ var root = document.documentElement var transitions = { 'transition':'transitionend', 'OTransition':'oTransitionEnd', 'MozTransition':'transitionend', 'WebkitTransition':'webkitTransitionEnd' } for (var t in transitions){ if (root.style[t] !== undefined ){ return transitions[t]; } } return undefined } //Example Usage: var transitionendevt = gettransitionend() if (transitionendevt){ // if transitionend event supported by browser element.addEventListener(transitionendevt, function(e){ // do something after end of transition }, false) }
The
event object in this case is populated with a few properties, two of
the more useful ones being event.propertyName
, which
returns a string containing the list of CSS3 property names that were
transitioned, and event.elapsedTime
, which returns the
duration of the transition in seconds.
The following demo uses CSS3 transition to animate a "progress bar"
underneath a button when clicked on. We use transitionend
to detect when the transition has finished, and alert either "Uploaded"
or "Unloaded" based on prcense of the "uploaded
" class at
the end of the affair.
Demo:
See the Pen transitionend event example by georgec (@georgec) on CodePen.
3.1) Detecting when a CSS keyframe animation has started, iterated, or ended
CSS Transition's older brother, CSS keyframe Animations lets us animate
CSS properties by defining points on a CSS "timeline" and the
participating CSS properties' values at those points. Using JavaScript,
we can similarly plug into the important states of a keyframe animation,
specifically, when a keyframe animation has started, iterated, or ended
completely. The relevant events are animationstart
,
animationiteration
, and animationend
, respectively.
Once again, to make nice with reality, we need to account for older
browsers supporting prefixed versions of the 3 events instead. With that
said, we can turn to the following function to return the supported
version of animationstart
, animationiteration
,
or animationend
:
function getanimationevent(suffix){ // enter "start", "iteration", or "end" var root = document.documentElement var suffix = suffix.toLowerCase() var animations = { 'animation': 'animation' + suffix, 'OAnimation': 'oAnimation' + suffix.charAt(0).toUpperCase() + suffix.slice(1), // capitalize first letter of suffix 'MozAnimation': 'animation' + suffix, 'WebkitAnimation': 'webkitAnimation' + suffix.charAt(0).toUpperCase() + suffix.slice(1) } for (var a in animations){ if (root.style[a] !== undefined ){ return animations[a] } } return undefined } // getanimationevent('start') // returns supported version of "animationstart" event as a string // getanimationevent('iteration') // returns supported version of "animationiteration" event as a string // getanimationevent('end') // returns supported version of "animationend" event as a string //Example usage: var animationend = getanimationevent('end') if (animationend ){ element.addEventListener(animationend , function(e){ // do something after end of keyframe animation }, false) }
The
event object is
once populated with a few properties, such as event.elapsedTime
, which returns the
duration of the keyframe animation in seconds.
The following demo uses CSS3 keyframe animatiion to create a simple bucket
fill animation. After each iteration of the animation, we count the
number of times the bucket has been filled by keeping track of the
number of times animationiteration
has been fired.
Demo:
See the Pen CSS animationiteration keyframe animation event demo by georgec (@georgec) on CodePen.
4) Disabling a CSS transition temporarily
This last tip is not really a JavaScript function, but rather, a CSS code block. Every now and then, it may be useful or necessary to temporarily disable a CSS transition while you adjust one the participating CSS properties values, which should happen instantly and behind the scenes. This post on Stackoverflow offers one solution to accomplishing this, by defining a "no transition" class in your CSS that you add to the element to temporarily stop any transitions on it:
.notransition { -webkit-transition: none !important; -moz-transition: none !important; -o-transition: none !important; transition: none !important; }
For example:
targetel.classList.add('notransition') // disable transitions targetel.style.left = '10px' // update "left" property but don't transition it targetel.offsetHeight // Trigger a reflow, flushing the CSS changes targetel.classList.remove('notransition') // enable transitions again targetel.style.left = '500px' // transition "left" property again
Just as important as adding the "no transition" class is to reflow
the element by accessing targetel.offsetHeight
(offsetHeight
is one of
several DOM properties that when accessed triggers an element
reflow) to force the browser to honor every change made to the element
in the same sitting.
Conclusion
CSS3 transitions/animations took visual effects on the web to new heights, by making itself highly accessible and easily deployable. As you can see, with a little JavaScript help, you can push that envelope even further, making it even easier to ditch a JavaScript framework such as jQuery to animate your web elements.