DEV Community

Cover image for How to animate an auto-height element
Nate Gibbons
Nate Gibbons

Posted on • Originally published at volumeintegration.com

How to animate an auto-height element

Originally posted at https://volumeintegration.com/animate-auto-height-element

Animating an auto-height element seems like it should be fairly straightforward, however it seems I'm not the only one who has struggled with this particular issue. The problem is usually some variant of the following:

  • I have some element I would like to allow to vertically expand and collapse.
  • The element has dynamic content - so therefore the height of the expanded element is unknown/dynamic.
  • I need to set the height of the element to auto to allow the element to change height based on its contents.
  • CSS doesn't allow transitioning to auto height so - it just jumps to the height when expanding/collapsing. No animation ☹️

 

This is what I want to do.

Showing auto-height expander

Some Workarounds

You may find several potential solutions to this problem if you spend a bit of time poking around the internet.

For example - there is the max-height workaround. In this solution you would basically transition the max-height attribute instead of the height. The trick is to set the final max-height to something way larger than you think the element will ever grow. This will effectively animate to the height of the element's contents. This might feel a little hanky to you - and for good reason. For starters - you have to guess what might be the largest the contents of the will ever get. But the content is dynamic - so that could easily get out of hand. Furthermore, the transition will animate to the full max-height specified. The visible height will stop at the height of the content - but the transition thinks it needs to grow all the way to the max-height. So for example - if you set a transition time of 300ms - it will take that long to animate to the full max-height even though the visual height stops well before then.

Other workarounds involve hiding the visual elements instead of changing the actual height or using javascript to manually animate/hide elements etc., but these are even more complicated than the max-height solution and introduce a slew of new problems to deal with (the very least of which is wreaking havoc on the element's accessibility).

My Hack Solution

If you're the kind of person that peeks at the end of the book (shame on you) then you can check out my working solution on codepen.

It still uses CSS to animate the height property through the transition property. However it also uses a bit of JavaScript to store some state for the element.

This solution will not work for all situations - but it suited my needs well, but there are some restrictions:

  • You must know the initial default height of the element. This means if you don't know what content will be in your div on initial load - this might not work so well. But if your element has an initial set of known contents this should work like a champ.
  • Content can only be added or removed from the element while it is in the expanded state. If content is added/removed from the div while collapsed - then you're out of luck again.

Assuming your needs fulfill these requirements - this should work nicely.

The solution essentially works like this:

  1. Store the initial height of the element in a variable somewhere. We'll call it expandHeight for now.
  2. When the element expands - you can easily transition the height from 0 to the expandHeight.
  3. After the transition is complete (use a setTimeout based on whatever you set the transition-duration property to) then set the element's height property to auto
  4. Add/remove content to the element as desired
  5. When collapsing -
    1. First store the element's current height back into the expandHeight variable.
    2. Next set the element's height back to a fixed value (what you just stored in expandHeight). This is because the element cannot transition from an auto height either. It can only transition to/from a fixed height.
    3. Now you can transition back to a height of 0.
  6. When you need to expand again - just start at step 2 above and repeat as necessary!

That's about all there is to it and it has worked well for me. One caveat is that you may need to stick step 5.3 in another setTimeout with a very small delay to allow the DOM time to register that the height attribute has changed from an auto height to a fixed height.

Here's my fully functioning example on codepen.

The astute observer might notice that it would not take too much imagination to create a high order ReactJS component out of this solution that stores its own state internally so you can re-use it anywhere with ease.

Let me know what you think. More importantly - let me know if you've got something even better! Cheers!

Feature Photo by Christian Kaindl

Oldest comments (8)

Collapse
 
oncode profile image
Manuel Sommerhalder

I also faced this problem writing my accordion library (github.com/oncode/handorgel). I solved it by using an inner div and animating the height of its parent element from zero to the offsetHeight of the inner div every time it is opened.

Collapse
 
marshallformula profile image
Nate Gibbons

That's a great idea! Would that mean that you can add content to the inner div while the outer div is collapsed?

Collapse
 
oncode profile image
Manuel Sommerhalder

Exactly, you can add the content while it's collapsed and it will open to the correct height.

Thread Thread
 
marshallformula profile image
Nate Gibbons • Edited

that's fantastic. I'm gonna try that out! Thanks!

Collapse
 
mrmurphy profile image
Murphy Randle

Does this still work if the parent element has display: none? It's good to hide the content from screen readers when it's collapsed (I'm assuming most screen readers skip content that's display: none).

Sorry for the subpar formatting. I'm writing from my phone.

Collapse
 
oncode profile image
Manuel Sommerhalder

Doesn't work if the parent element has display: none. Also the animation won't work.

But when you remove "display: none" prior to opening and after closing (when transition has finished), you can add "display: none" and everything will work. I set display: none like this in my library to avoid screen readers reading the content and preventing that elements can be focused (e.g. when tab is used).

Thread Thread
 
mrmurphy profile image
Murphy Randle

Right, so you can't use getBoundingClientRect to get the size of the child container when the parent has display: none?. So you have to first remove display:none, then measure, then animate?

Thread Thread
 
oncode profile image
Manuel Sommerhalder • Edited

Exactly, all getBoundingClientRect properties would be 0 when the parent has display: none.