CSS Based fadeOut Effect

Preface

In this post, I will discuss an approach to replace JavaScript based animations with CSS transitions — specifically jQuery’s fadeOut method. The resulting code is not offered as a production ready solution, as it does not account for browser fragmentation or standards adoption. Instead, I am exploring a concept.

Let’s begin

Before CSS3, we relied on JavaScript, or more often the popular library jQuery, to handle our animations.

Animations are great. When properly used they can often increase the user’s understanding of what is happening on the page. For example, removing an element from the page with a fadeout animation is better than instantaneously hiding it.

With jQuery, fading out an element is straightforward:

$('#element').fadeOut();

Simple. But before we move on, let’s talk a little bit about what’s happening above. The jQuery fadeOut method is pretty self-descriptive. It gradually lessens the opacity of the target element — in this case the element with an id element — from 1 (fully opaque) to 0 (fully transparent). Since we did not specify a transition duration or easing function the defaults of 400 milliseconds and swing1 are used respectively. Finally, from the jQuery documentation:

Once the opacity reaches 0, the display style property is set to none, so the element no longer affects the layout of the page.

All is good, but JavaScript powered animations are so old-fashioned. We want to use the new shiny CSS Transitions! Sadly however, things become a little more complicated. Let’s looks at a first attempt at making an element fade out using CSS transitions:

#element {
  transition: opacity 0.4s linear;
}

#element.invisible {
  opacity: 0;
}

So far, so good: whenever the element has the class hidden applied to it (say via a JavaScript event), it will slowly fade out (its opacity will dimish to 0) over a duration of 400 milliseconds. The linear keyword specifies the transition’s easing function.

However, we’re missing the last bit from jQuery’s implementation: once the element is finshed transitioning its opactity propertity we need to remove it from the page flow. jQuery does this by setting the element’s display property to none.

#element.invisible {
  opacity: 0;
  display: none;
}

Hang on! This won’t work. Why? Because the display: none; value will be applied immediately once the #element element acquires the hidden class. You will never see the fadeout effect specified by the transition. The element will be taken out of the page flow before you perceive the animation’s beginning.

What we want is to apply the display: none; property after the CSS transition has completed. How can we do this?

One possible answer would be to use another transition property: transition-delay. A transition delay allows us to offset the beginning of the transition by a specific unit of time. The idea here is to delay removing the element from page flow by the same amount of time we are transitioning the opacity property of the element, and setting its transition duration to 0s. In other words, instantaneously apply the transition but only after a specified delay.

Unfortunately, we hit another snag. Only animatable properties can be the subject of a CSS transition, and the display property isn’t on the list. If the property we wanted to sync with the opacity animations ending time was animatable, we’d be set.2

Looks like we need to use a bit of JavaScript.

The W3 CSS Transitions spec defines a JavaScript transition event system. Ok great! We will now define a new class to be applied upon on transitionend event. Let’s call this opacity-transition-ended. Here’s the JavaScript:

var element = document.getElementById("element");
element.addEventListener("transitionend", function () {
  element.className += " hidden";
}, true);

And define the CSS like so:

#element {
  transition: opacity 0.4s linear;
}

#element.invisible {
  opacity: 0;
}

#element.invisible.hidden {
  display: none;
}

Compatibility

What about compatibility? As this is a CSS3 based approach, browser compatibility is somewhat limited. CSS Transitions are supported in Firefox 4+, Internet Explorer 10+, Safari 3.2+, Chrome 1.0+, and Opera 10.5+. This also means, vendor prefixes and browser specific event names. See this MDN compatibility table for the specifics.


  1. swing is jQuery’s default easing function. Here’s a Stackoverflow conversation describing the swing function as well as a strategy for how to go about implementing it in terms of a CSS transition. 

  2. It would be great if the spec allowed you to use non-animatable properties if (and only if) the transition-duration property of the transition was set to 0. That way you wouldn’t need to rely on JavaScript.