Using Transition Events in JavaScript

May 22, 2015
Javascript, Css, Animation
~3min
Share on Twitter

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 permalink

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.


            javascript
            
          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.

What Could I Do With It? permalink

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 opacity is animated from 0.5 to 1 and this takes 1 second with the following CSS.


            css
            
          el { transition: fadeIn 1s ease 0s; }

Once a transition has happened, we can use the transitionend event to fire some code.


            javascript
            
          el.addEventListener('transitionend', function(e) {
  if (confirm('Seen me?')) el.style.opacity = 0.5
});

This still isn’t ideal because we are relying on knowing the CSS declared opacity 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.


            css
            
          el            {
                transition: opacity 0s ease 0s;
                opacity   : 0.5;
              }
.is__shown    { opacity   : 1; }
.is__active   { transition: opacity 1s ease 0s; }

We add the classes to trigger the animation on say a user click


            javascript
            
          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.


            javascript
            
          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 permalink

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;


            javascript
            
          el.addEventListener('transitionend', function(e) {
  el.className = '';
  if (!done) { el.className += ' is__active is__moving'; }
});

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.

How Do You Get Around This? permalink

It’s a basic solution;


            javascript
            
          el.addEventListener('transitionend', function(e) {
  el.className = '';
  if (!done) {
    setTimeout(function(){
      el.className += ' is__active is__moving';
    }, 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 permalink

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? permalink

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.