Categories:

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 getcss3prop() 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.