Using transition events in JavaScript
TL;DR I made a carousel type component using the transitionend event and vanilla JavaScript, here’s some notes about using the transitionend event and how I overcame a particular tricky behavior
CSS animations and transitions are becoming commonplace.
But, there are times when you need tighter control over transitioning elements or you need to be able to trigger some behaviour once something has finished sliding around the screen or spinning.
Enter the transitionend event.
Note:: there is also an animationend event that will fire when an element has finished an animation using a keyframe. I believe this can be used in much the same way as transitionend.
transitionend
The transitionend event allows you to listen for the moment an element stops a transition.
There is not much more to it. Transition an element, listen for the transition to end, fire some logic.
var el = document.getElementById('element');
el.addEventListener('transitionend', function(e) {
console.log('Look I didz a transition!!!111');
});
If your browser supports it, you can use it. Check out the browser support here.
so what could I do with it?
You might not even have any animation or transitions happening on your page so you certainly won’t need transitionend.
Even if you do have some, it’s not an event you’ll likely need or can force into your code. But, there can be scenarios where it might be handy.
A use case might be resetting the state of an element once it has transitioned.
For example; let’s say we have an element whose width is animated from 100px to 200px and this takes 1 second with the following CSS.
el { transition: width 1s ease 0s; }
Once a transition has happened, we can use the transitionend event to reset the element state.
el.addEventListener('transitionend', function(e) {
el.style.width = 100px;
});
This still isn’t ideal because we are relying on knowing the CSS declared width of the element within our JavaScript and this is a coupling that is undesired.
Instead we should define classes and then make use of transitionend to manage the appropriate classes.
el {
transition: width 0s ease 0s;
width : 100px;
}
.is__extended { width : width 200px; }
.is__active { transition: width 1s ease 0s; }
We add the classes to trigger the animation on say a user click
el.className += 'is__extended is__active';
And once the transition has taken place, reset the element by removing these classes, essentially meaning that the element will revert to its previous base style.
el.addEventListener('transitionend', function(e) {
// removing classes by just wiping the className
el.className = '';
});
I’ve put together a simple demo which can be seen here.
chaining transitions
One scenario that you could come across is wanting to chain transitions based on some internal state or behaviour.
If we think about this, essentially inside our transitionend callback we wish to trigger the code that initially made our transition fire.
For example;
el.addEventListener('transitionend', function(e) {
el.className = '';
if (!done) { el.className += ' is__active is__extended'; }
});
Logically, this looks correct and you’d think it might work but it will not.
I recently encountered this whilst creating a carousel like component where by I wanted to be able to transition to a particular slide of content based on a target id. This would entail chaining the transition until I got to my target. You can imagine the code to be somewhat similar as to above but I was checking to see if the current slide after a transition matched my target slide and if not keep going.
So how do you get around this?
It’s a simple one;
el.addEventListener('transitionend', function(e) {
el.className = '';
if (!done) {
setTimeout(function(){
el.className += ' is__active is__extended';
}, 0);
}
});
That’s right, you wrap the trigger in a zero millisecond timeout and it will re-trigger the animation for you therefore enabling you to chain your transitions from JavaScript either infinitely or until a condition is met without having to use animation keyframes.
I’ve put both versions in the demo.
Thinking about it, you could use animation keyframes but you’d want to intervene at the end of a keyframe animation loop exactly and I haven’t investigated just quite how possible this is.
beware when using browsersync
One thing I will point out and it may just be a problem that I encountered personally.
I was working on a project making use of transitionend using browser-sync and gulp.
I had a transition trigger on click, if I mashed the click the transitions would work as expected but then after they finished I would get some extra transitions in a sort of lag like effect.
I tried debouncing, throttling, adding guards to stop the callback firing…
What was the problem?
I had my project open in multiple tabs. Once I went back down to one tab, all was good and it worked fine. Multiple instances on the same page work fine too. So whether there is something strange happening with my setup or there is some quirk with browsersync, beware!
The transitionend event provides a pretty cool way to manage your transitioning elements in JavaScript so be sure to check it out if it’s the right fit for you.
As always, any questions or suggestions, please feel free to leave a response or tweet me 🐦!