Rick Strahl's Web Log

Wind, waves, code and everything in between...
ASP.NET • C# • HTML5 • JavaScript • AngularJs
Contact   •   Articles   •   Products   •   Support   •   Search
Ad-free experience sponsored by:
ASPOSE - the market leader of .NET and Java APIs for file formats – natively work with DOCX, XLSX, PPT, PDF, images and more

Using CSS Transitions to SlideUp and SlideDown


In a mobile app I’ve been working on there are a few animations to drop down additional content as needed when clicking on an item. The effect is basically that I touch or click on an item and that then a menu drops down below. Another click or touch and the menu slides back up.

Here’s what it looks like (from geocrumbs.net):

ScreenShot

In the past I’ve used jQuery’s .slideDown() and .slideUp() methods to do this sort of thing, but if you actually try running that on most phones today, you’ll find that the animation is pretty jerky as it runs without hardware rendering assistance using only the CPU rather than the GPU.

CSS Transitions 101

CSS transitions often make it easy to replace effects with CSS animations that render much smoother on slower devices and computers assuming you’re running a browser that’s reasonably recent. Most CSS transitions are super easy to create and use.

For example here’s a simple fade in and fade out animation tied to a a couple of CSS classes (example on Plunker):

.fadein, .fadeout {
    opacity: 0;
    -moz-transition: opacity 0.4s ease-in-out;
    -o-transition: opacity 0.4s ease-in-out;
    -webkit-transition: opacity 0.4s ease-in-out;
    transition: opacity 0.4s ease-in-out;
}
.fadein {
    opacity: 1;
}

Transitions work by processing a change of a value of a CSS property. Any property that has numeric values works can be transitioned in this way. Some examples are opacity, height and width, color values etc. You specify the CSS property a time frame and the easing mechanism which determines the how the speed of the transition is balanced. Ease in and out for example slowly ramps to a peak, then slows down as the transition ends.

 <div class="container" style="padding: 40px">
        <button id="Trigger2">Trigger FadeIn/FadeOut</button>
        <div id="Fader" class="fadeout">
            Hello World Text
        </div>
 </div>

And it’s then super easy to simply add and remove the appropriate styles using either other CSS classes or explicit script triggers. Here’s an example using a code trigger using jQuery:

$("#Trigger2").click(function () {
    if ($("#Fader").hasClass("fadeout"))
        $("#Fader").removeClass("fadeout").addClass("fadein");
    else
        $("#Fader").removeClass("fadein").addClass("fadeout");
});

And voila you have a fairly generic fade-in/fade-out transition that is essentially declarative – simply by using a couple of CSS classes. If you’re using a binding framework like AngularJS, Knockout, Ember etc. you can bind these classes directly to affect behavior, otherwise a couple of lines of JS code will do the trick.

You can animate a ton of CSS attributes in this way and it’s pretty straight forward to do this with most of them.

Height and Width Animations are more complex

You can also animate height and width using CSS transitions and the logic to set this up is the same. But there are a number of added complexities when using height and width animations because DOM elements are made up using the DOM Box model which projects the element’s width and height, plus the padding and borders etc. While it’s easy to animate height and width, to animate them to become invisible and the resize themselves to the proper size is actually a not as obvious as it seems.

I’m going to skip all of my false starts here – suffice it to say that some of the obvious animation paths using height and width directly didn’t work very well, because in order to hide the element I have to remove the padding. Then when making it visible the padding has to be added back in, but when the padding is added it the padding expands immediately, bypassing the animation transition. If the height is greater you’d see the padded element immediately followed by the remainder rendered with the transition. No good.

The better solution is to use the max-height property along with the overflow-y property (or max-width and overflow-x) to limit the sizing and removing any padding and margins by wrapping the element to be animated into another element that doesn’t have either padding or margins.

Here’s what this looks like in an example (and you can check out the example on Plunker):

<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <style> #Slider { } #Actual { background: silver; color: White; padding: 20px; } .slideup, .slidedown { max-height: 0; overflow-y: hidden; -webkit-transition: max-height 0.5s ease-in-out; -moz-transition: max-height 0.5s ease-in-out; -o-transition: max-height 0.5s ease-in-out; transition: max-height 0.5s ease-in-out; } .slidedown { max-height: 60px ; } </style> </head> <body> <div class="container" style="padding: 40px"> <button id="Trigger">Trigger Slideup/SlideDown</button>
<div id="Slider" class="slideup"> <!-- content has to be wrapped so that the padding and margins don't effect the transition's height --> <div id="Actual"> Hello World Text </div> </div> </div> <script src="scripts/jquery.js"></script> <script> // slideup/slidedown $("#Trigger").click(function () { if ($("#Slider").hasClass("slideup")) $("#Slider").removeClass("slideup").addClass("slidedown"); else $("#Slider").removeClass("slidedown").addClass("slideup"); }); </script> </body> </html>

There are few tricky things going on here to make this work.

First there are the .slideup and .slidedown CSS classes with slideup being the closed mode. Note they basically set up the max-height and the transition. In closed mode max-height is set to 0 and overflow-y hidden which effectively hides all content. The slidedown class then sets the height to a specific height. Unfortunately you have to use a specific height here and that height has to be big enough to contain the largest content you expect to show and the size should be as close as possible to the size of your actual content. The animation runs to the max-height specified – if you’re content is smaller than the max-height the animation keeps going after your content has been shown. If it’s too big it the content will be cut off. Choosing the right size here is key.

You could also trigger the max-height via code if you need to calculate, but that’s not easy if the content is hidden or collapsed ($.outerHeight() returns 0 until the content actually resizes! More on this in the next section).

The next tricky thing is that you need to wrap your content – if you have any padding or margins. The wrapper effectively removes the padding from the wrapping container so that max-height only applies to the calculated height of the element. If you have no padding or margins then your content can go directly inside of the animated container. Otherwise the container guarantees the the wrapped element is completely hidden.

Finally the code to actually trigger the animations simply removes one class and replaces it with the other to invoke the effect.

This works well and easy to understand. The only downside is the max-height setting which has to be set to an explicit pixel value. We’ll address that in the next iteration.

Posted in CSS  JavaScript  HTML5  

The Voices of Reason


 

lespauled
February 24, 2014

# re: Using CSS Transitions to SlideUp and SlideDown

I'm probably incorrect, but couldn't you use toggleClass for fadeIn?

Rick Strahl
February 24, 2014

# re: Using CSS Transitions to SlideUp and SlideDown

@lespauled - I took a quick look on the second Plunker (manual slide down sample) and replaced the clickhandler with:

        $("#Trigger").click(function () {
          $("#Slider").toggleClass("slideup slidedown");
        });


and that does work. I had to refresh my memory on .toggleClass() - I had forgotten that it can work with multiple classes and that does indeed work. 

Cayce k
June 12, 2014

# re: Using CSS Transitions to SlideUp and SlideDown

Hey!

Thanks for this. I found an easier pure CSS way of doing the slide that I don't have time posting to any other sites right now, but might be good for you to use.
#footer-secondary-menus {
    bottom: 40px;
    overflow: hidden;
    position: absolute;
    width: 100%;
    max-height: 0;
 
    -webkit-transition: all 1s ease-in-out;
    transition: all 1s ease-in-out;
}
 
#footer-secondary-menus.active{
    max-height: 400px;
}


I'm using it for menus right now. But the content inside has all the styling for the menus and this div just holds that content. By setting max-height to 0 like you had obviously forces it to 0 on off and then I use toggle jQuery to add the class for clicking reasons. (That one is obvious that you can't change..)

By setting the max-height in active to a value just a bit higher than my predicted max-height any way it will force the content to slide up at a pace equivalent to fill the max-height. This means don't put a super large number or this won't work unless there is a ton of content. Hopefully this helps someone.

Peter Hobbs
October 26, 2014

# re: Using CSS Transitions to SlideUp and SlideDown

Great job here Rick. Your excellent article made my weekend. Really appreciate your detailed explanation.

nonbrake
November 23, 2014

# re: Using CSS Transitions to SlideUp and SlideDown

Hi, you have to add resize function for resizing window then counted outerHeight isn´t correct after window resizing.

Saurabh Udaniya
April 05, 2015

# re: Using CSS Transitions to SlideUp and SlideDown

you are setting height as
element.css("max-height", height); 
I tried your solution with angular directive and it was not working, however adding "px" to it
element.css("max-height", height+"px"); 

saved my time so thanks for this article.

Madhu
November 03, 2015

# re: Using CSS Transitions to SlideUp and SlideDown

when i click open button open a div from middle like(clip),above the div shows close buttton to close the div like clip,opening and closing dive effects like clip not a slideup or slidedown

Tim
July 25, 2016

# re: Using CSS Transitions to SlideUp and SlideDown

In the second example when the max height is set via jquery, any ideas why temporarily setting the height to none doesn't momentarily show on the element before being set back to 0?

Rick Strahl
July 25, 2016

# re: Using CSS Transitions to SlideUp and SlideDown

@Tim - DOM changes inside of a function aren't applied visually until the function finishes execution. In this case changing the value is done so the element is in the proper state, but the change never is applied to the visual tree that actually displays in the browser. The end result is that the effect is never displayed.

This is quirky behavior and one of the reasons why setTimeout() is needed in many cases in JavaScript - setTimeout() delays ensure that the previous changes have been applied to the visual tree before executing subsquent command in the setTimeout() function which runs in a new execution window and DOM update cycle.

Roy
March 09, 2017

# re: Using CSS Transitions to SlideUp and SlideDown

Hello,

Great tutorial! It works great for me. But it doesn't seem to work in Safari (iphone and macbook). I tested with alert and it seems that Safari doesn't calculate the outerHeight. Does anybody know a solution?


Rick Strahl
March 09, 2017

# re: Using CSS Transitions to SlideUp and SlideDown

Yeah it looks like in iOS 10 that somehow got broken... maybe using innerHeight and a wrapping container with no padding or margins?


Roy
March 10, 2017

# re: Using CSS Transitions to SlideUp and SlideDown

Tnx for the quick reply. I tried several things, but in my case nothing really worked. So I found another solution. I do not calculate the max-height with Javascript, but I use a fixed max-height. For example:

 setTimeout(function() {
    $el.css({"max-height": "99999px"});
 }, 20);

For me it is enough. The container will not be any larger than the content that it contains. I still use the delay, because it combines nice with the scroll animation I also use.


Rick Strahl
March 10, 2017

# re: Using CSS Transitions to SlideUp and SlideDown

Thanks Roy, for taking a look at that. Going to try it out and if it works add it to the article.


Saikat Das
April 06, 2017

# re: Using CSS Transitions to SlideUp and SlideDown

I used this and is perfectly working fine on our website. But, this thing is not working on mobile devices and on tablets.. how do i fix this..?? please help

 

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2017