CSS Transition Auto Height

CSS Transition With A Little Help From jQuery

Enable a real CSS transition to and from height:auto by filling in the natural height of the element during the transition with a bit of jQuery.

There are any number of potential use cases for a transition to and from height:auto, for example a simple expanding/collapsing menu:

Along with using CSS to animate the transition, one of the benefits of this technique is that the HTML and CSS are exactly as they would be if a native CSS transition to/from height:auto was supported. A bit of jQuery simply fills in the element's natural height during the transition.

Jump To The Code

The HTML

The exact configuration can be adjusted as needed, but most cases will include the content element to be transitioned and a clickable element, like a button, to trigger the CSS transition.

For example an expanding/collapsing menu:

HTML
<button class="menu-toggle" type="button">Menu</button>

<ul class="menu">
    <li><a href="...">Menu Item A</a></li>
    <li><a href="...">Menu Item B</a></li>
    <li><a href="...">Menu Item C</a></li>
    <li><a href="...">Menu Item D</a></li>
</ul>

The CSS

The open and closed states, including height values and transition options are managed entirely in CSS - arguably as they should be - and applied with an .open class.

The clickable toggle element and the expanding element can be otherwise styled as desired, but the basic functionality includes the open and closed states and transition values:

CSS
/*--- button ---*/
.menu-toggle {...}

    /*--- open state ---*/
    .menu-toggle.open {...}

/*--- menu ---*/
.menu {visibility:hidden;height:0;overflow:hidden;transition:visibility 0s .2s, height .2s}

    /*--- open state ---*/
    .menu.open {visibility:visible;height:auto;transition-delay:0s}

Each CSS declaration in more detail:

The jQuery

With the open and closed states managed in CSS, JavaScript/jQuery is only needed to add and remove the .open class(es) on click:

CSS
// menu reveal
$('.menu-toggle').on('click', function(){
    $(this).add('.example-menu').toggleClass('open');
});

The HTML, CSS and jQuery so far expands and collapses, but the CSS height property doesn't natively transition to the auto value so the transition to and from the .open state is instantaneous, rather than with the desired animated CSS transition.

A Little Razzle Dazzle

Fortunately, a little jQuery with the transitionend event and .scrollHeight property can make up the difference.

Open, close and combined functions form a reusable mini-plugin:

jQuery
// open
function heightopen(){
    $(this).height($(this).get(0).scrollHeight).addClass('open'); // get height and open
    $(this).one('transitionend', function(){ // after transition complete
        $(this).height(''); // revert to CSS-set height
    });
}

// close
function heightclose(){
    $(this).height($(this).get(0).scrollHeight).height('').removeClass('open'); // get height and close
}

// open & close based on open state
function heightopenclose(){
    if($(this).hasClass('open')) {
        $(this).each(heightclose); // close
    }
    else {
        $(this).each(heightopen); // open
    }
}

And minified:

jQuery
function heightopen(){$(this).height($(this).get(0).scrollHeight).addClass('open'); $(this).one('transitionend',function(){$(this).height('')})}function heightclose(){$(this).height($(this).get(0).scrollHeight).height('').removeClass('open')}function heightopenclose(){if($(this).hasClass('open')){$(this).each(heightclose)}else{$(this).each(heightopen)}}

(The function names can be shortened to make this snippet even smaller.)

Updated Click Function

One last step. The snippet to add/remove the .open class on click can then be updated to use the new heightopenclose function:

CSS
// menu reveal
$('.menu-toggle').on('click', function(){
    $(this).toggleClass('open'); // optional (for clickable element open state)
    $('.menu').each(heightopenclose);
});

And that's it - one small snippet of jQuery effectively enables CSS transitions to and from height:auto for any number of use cases.

Here it is in action with a simple expanding/collapsing menu:

Reflow

Although not an issue for absolutely-positioned elements, animated or not, expanding/collapsing or other potential layout shifts should be used sparingly to avoid the performance impact of content reflow.

More Helpful JavaScript & jQuery

Transitioning to/from height:auto is a great application of jQuery, but there are other ways JavaScript can improve user experience, like page speed.

Images are often the largest resources on the page, so lazy loading is a simple and valuable strategy to improve loading speed:

Lazy Loading Images

Using embedded YouTube videos? Eliminate the impact on initial-load page weight and page speed by loading embedded video on demand:

On-Demand Embedded Videos