DEV Community

Cover image for Solving Animation Layout Flickering Caused by CSS Transitions
Steven Woodson
Steven Woodson

Posted on • Originally published at stevenwoodson.com on

Solving Animation Layout Flickering Caused by CSS Transitions

I recently had the opportunity to redo the header section of an inherited codebase. Everything was going great, my version was nearly 30% less code, more accessible with larger clickable targets and focus handling, and life was good.

But then it happened…

The Layout Flickering Problem

As I’m testing in a few browsers I noticed the mobile version of the header menu was flickering closed when resizing from the desktop to the mobile breakpoint. A quick debug later and I realized it was because of the transition CSS property I have set on the menu so it’ll slide out and fade in when opened.

Specifically, that transition is triggered when going from desktop to mobile because I’m visually hiding it once it gets to a point where it needs to be opened with a button. When those styles are applied, the transition animation triggers.

Before Example

Here’s a live example of the CSS animation flashing I’m talking about:

I didn’t want to remove the CSS transition, but I also didn’t want this weird animation flashing on resize, so what do I do?

The Prevent Animation Transition Solution

I wasn’t very happy with any of the solutions I found out there, they were some variation of:

  • really heavy-handed, like removing all transitions while the browser is resized
  • relied on fragile solutions like setTimeout
  • “just don’t use animation 🤷‍♀️”

So I got to work. The obvious problem was the transition applying when adding the mobile-only styles so there’s gotta be a way to prevent just that one use case, right? You bet there is, and it’s only two small changes!

Isolate transition Into a Unique Class

First, we need to isolate the transition separately from the styles that are applied when the menu is opened. In my case it was a new class called nav-opening:

.nav-opening {
  transition: all 0.5s ease;
}
Enter fullscreen mode Exit fullscreen mode

Add & Remove the Transition

Second, we need to add the transition-only nav-opening class when adding the open class nav-opened. This happens on click of the mobile menu button:

this.el.classList.add('nav-opening');
this.el.classList.add('nav-opened');
Enter fullscreen mode Exit fullscreen mode

And then we need to remove nav-opening when the animation is done closing, for this I turned to transitionend. That way I don’t have to guess with something like setTimeout, I trigger the removal of that class when I know the animation is done. Like this:

this.el.addEventListener('transitionend', this.setFocus.bind(this));

SlideOutMenu.prototype.setFocus = function (e) {
  if (e.target == this.el && e.propertyName == 'opacity') {
    const compStyles = window.getComputedStyle(this.el);
    if (compStyles.opacity == 1) {
      this.firstTabStop.focus();
    } else {
      this.openButton.focus();
      this.el.classList.remove('nav-opening');
    }
  }
};
Enter fullscreen mode Exit fullscreen mode

The setFocus function here is checking that the animation transitionend happened on the element we care about, and only triggering for the opacity transition. The latter is just in case you have multiple transitions happening (opacity, left, visibility, etc.), you don’t want this running for all of them every time.

After Example

Here’s the same example with the above modifications applied, no more layout flickering on resize!

Wrapping Up

The code examples in CodePen were intentionally kept lean on styles and uses vanilla JavaScript in the hopes that you can more easily make use of it in your project. Be sure to copy from the After example, otherwise I wrote this whole blog post for nothing!

I wanted to also note that, while a bit too much for my use case, I do really like this Stop Animations During Window Resizing example that Chris worked up. If you have a lot going on transitions-wise this may be a cleaner solution for you than having to manage each piece individually.

Further Reading

Latest comments (0)