The TransitionEnd Event

by kirupa   |   4 January 2013

While a transition is something you would bucket squarely in the CSS category, there is a certain level of co-operation between it and JavaScript. Events is one area where this cooperation really shows itself.

You can think of a transition as having three states:

transition stages

 

There is a start state, transitioning state, and the end state. Now, this is where I tell you an event exists for detecting all three states. Unfortunately, that isn't the case. You will have to settle for only one state for which an event exists, and that is the end state. That event is affectionately called transitionend, and this tutorial will tell you all about it.

Meet the transitionend Event

The way the transitionend event works is pretty simple. This event is fired by the element the transition is affecting once the transition has finished. Let's say you have some CSS that looks as follows:

#blueCircle {
	transition: all .5s ease-out;
	opacity: .3;
}
#blueCircle:hover {
	transform: scale(3);
	opacity: 1;
}

In this CSS, we define a transition that affects all properties on the blueCircle element over a period of half a second. When you hover over the blueCircle, we set the transform property to scale the circle by 300% and set the opacity to 1. If you can imagine how this works, when you hover over the blueCircle element, the element will grow larger and become more visible over a period of .5 seconds. Pretty straightforward stuff.

Let's say you wanted to detect when this transition ends. To detect when this transition ends, you will listen to the transitionend event as it is fired by the blueCircle element. The code for doing that looks as follows:

var blueCircle = document.querySelector("#blueCircle");

blueCircle.addEventListener("transitionend", detectTheEnd, false);
blueCircle.addEventListener("webkitTransitionEnd", detectTheEnd, false);
blueCircle.addEventListener("mozTransitionEnd", detectTheEnd, false);
blueCircle.addEventListener("msTransitionEnd", detectTheEnd, false);
blueCircle.addEventListener("oTransitionEnd", detectTheEnd, false);

function detectTheEnd(e) {

}

With the exception of the vendor prefixing, all of this should be pretty straightforward stuff involving events and event handlers. When the transitionend event is detected, the detectTheEnd event handler gets called.

The transitionend Event Handler

What is less straighforward is what happens inside the event handler for this event. Your transitionend event doesn't just fire once when the transition is over. It fires each time for every CSS property that your transition is affecting. In our case, the transitionend event is fired twice - one time for the opacity property and another time for the transform property.

Because the event gets fired twice, what this means is that the event handler gets called twice as well. More than likely, you do not want your transitionend event handler to be called twice - at least not for the same transition with the same duration. You cannot prevent the event handler from being called twice. What you can do, though, is ensure the code inside the event handler is called only the number of times you want it to.

One way to ensure that is by checking the event argument's propertyName value for the CSS property the event is representing. Here is how that would look like when applied to our particular situation:

function detectTheEnd(e) {
	if (e.propertyName == "opacity") {
		// do something interesting
	}
}

Even though the detectTheEnd event handler will get called twice, the code I care about will only execute once when the if statement evaluates to true. It will execute only when the event handler is called as a result of the transitionend event firing for the opacity property.

Dealing With Multiple Transitions

For a single transition affecting multiple properties (like what we have in our example), inside your event handler, things are pretty simple. You do not need a propertyName check for any other properties such as transform. The reason is that the transitionend events will be fired almost immediately after each other since the transition ends after .5 seconds for both the opacity and transform properties. There is no point in breaking your code up.

There is a point in breaking your code up if you had multiple transitions each affecting different properties with different durations. In this case, your transitionend events would be fired at different times, and you may want to handle each firing of the event differently depending on the CSS property that just finished transitioning.

Below is some code on how we can handle this situation where we now have two transitions each ending at a different time:

#blueCircle {
	transition: opacity .5s ease-out, transform 1s ease-in;
	opacity: .3;
}
#blueCircle:hover {
	transform: scale(3);
	opacity: 1;
}

In this situation, your transitionend event will be fired once at the .5 second mark and again at the 1 second mark. If you wanted to react to each instance of the transitionend event separately, you would expand the code I had shown earlier as follows:

function detectTheEnd(e) {
	if (e.propertyName == "opacity") {
		// do something interesting
	} else if (e.propertyName == "transform") {
		// do something interesting
	}
}

By being deliberate in checking for the propertyName, you ensure the right code gets executed depending on which transitionend event got fired.

Conclusion

At first glance, knowing when a transition ends does not seem particularly important. As most first glances go, that is partially accurate. You will not commonly care when a transition ends. Uncommonly, though, knowing when a transition ends is extremely useful. This is especially true when you are dealing with creating looped animations that involve a transition. In a subsequent tutorial, I will highlight how listening to the transitionend event allows you to create some really REALLY interesting animations that would have simply been too hard to do otherwise.

Getting Help

If you have questions, need some assistance on this topic, or just want to chat - post in the comments below or drop by our friendly forums (where you have a lot more formatting options) and post your question. There are a lot of knowledgeable and witty people who would be happy to help you out

Share

Did you enjoy reading this and found it useful? If so, please share it with your friends:

If you didn't like it, I always like to hear how I can do better next time. Please feel free to contact me directly via e-mail, facebook, or twitter.

Kirupa Chinnathambi
I like to talk a lot - A WHOLE LOT. When I'm not talking, I've been known to write the occasional English word. You can learn more about me by going here.

Add Your Comment (or post on the Forums)

blog comments powered by Disqus

Awesome and high-performance web hosting!
BACK TO TOP
new books - yay!!!