Animating max-height with CSS transitions
Asked Answered
B

7

77

I want to create an expand/collapse animation that's powered only by classnames (javascript is used to toggle the classnames).

I'm giving one class max-height: 4em; overflow: hidden;

and the other max-height: 255em; (I also tried the value none, which didn't animate at all)

this to animate: transition: max-height 0.50s ease-in-out;

I used CSS transitions to switch between them, but the browser seems to be animating all those extra em's, so it creates a delay in the collapse effect.

Is there a way of doing it (in the same spirit - with css classnames) that doesn't have that side-effect (I can put a lower pixel count, but that obviously has drawbacks, since it might cut off legit text - that's the reason for the big value, so it doesn't cut off legit long text, only ridiculously long ones)

See the jsFiddle - http://jsfiddle.net/wCzHV/1/ (click on the text container)

Bioecology answered 16/4, 2013 at 4:37 Comment(4)
Not perfect solution: jsfiddle.net/wCzHV/2 Is this acceptable?Armyworm
@Armyworm - it might be possible with negative margin, but your example doesn't seem to support having arbitrary height or exposing a preset number of lines (both these things are supported in the original and are essential)Bioecology
That's why I don't post that as an answer. This question has somewhat been asked on SO, and I would say that it's almost impossible to achieve in pure CSS (if you don't want to hard-code some value). If you really really need to do that, involve JS.Armyworm
I was worried that it might not be possible, that's too bad, I hate to see it almost workingBioecology
B
2

In case anyone is reading this, I have not found a solution and went with an expand-only effect (which was achieved by moving the transition style to the expanded class definition)

Bioecology answered 17/4, 2013 at 8:14 Comment(2)
would you mind posting the solution in a JS Fiddle or similar?Congresswoman
It's 2016 and it still doesn't have a solution. It's a shame.Jefferson
T
136

Fix delay solution:

Put cubic-bezier(0, 1, 0, 1) transition function for element.

scss

.text {
  overflow: hidden;
  max-height: 0;
  transition: max-height 0.5s cubic-bezier(0, 1, 0, 1);

  &.full {
    max-height: 1000px;
    transition: max-height 1s ease-in-out;
  }
}

css

.text {
  overflow: hidden;
  max-height: 0;
  transition: max-height 0.5s cubic-bezier(0, 1, 0, 1);
}

.text.full {
  max-height: 1000px;
  transition: max-height 1s ease-in-out;
}
Trinitarianism answered 23/8, 2016 at 14:17 Comment(10)
Best way to do it pure-css. Thanks for the reminder, lost that curve a while ago.Trangtranquada
Could someone explain what does &.full mean in this context?Liba
@Jakub ''&.full" refers to the final state of the text class, so when it has reached its full sizeDiscourse
@Jakub &.full is saying - "When the class .full gets added to an element with the class .text, update the CSS as follows..." Its basically the SCSS way of doing this .text.full {}, which is how it would look in normal CSS. It's called nesting and is a feature of Sass/SCSS.Pluperfect
@Pluperfect thanks, shouldn't it be closed with another curly bracket at the end?Liba
@Jakub yes it should be.Pluperfect
This answer desperately needs an explanation. It's upvoted quite a bit, but it doesn't seem to work properly. The only effect I'm seeing, is that the retracting transition is only 0.5s vs the expanding transition that's 1s.Brumby
After looking into it more, I can say with some certainty that this is not a fix, it's a workaround, and it's not even a good one. The only things happening are basically the shorter duration of the animation, and the fact that the function being used is basically an extreme ease-out. So basically, you're just contracting those 1000px of max height really quickly, and slowing down in the end, so the final bit looks somewhat like a regular ease-in-out. Again: not a fix, but a workaround, and not a good one.Brumby
While implementing this, I found it wasn't working with max-height: auto. Is there a specific reason for it ?Minesweeper
@SalimShamim transition works only with numeric values as for (max-min)height and (max-min)widthIncisive
S
12

This is an old question but I just worked out a way to do it and wanted to stick it somewhere so I know where to find it should I need it again :o)

So I needed an accordion with clickable "sectionHeading" divs that reveal/hide corresponding "sectionContent" divs. The section content divs have variable heights, which creates a problem as you can't animate height to 100%. I've seen other answers suggesting animating max-height instead but this means sometimes you get delays when the max-height you use is larger than the actual height.

The idea is to use jQuery on load to find and explicitly set the heights of the "sectionContent" divs. Then add a css class 'noHeight' to each a click handler to toggle it:

$(document).ready(function() {
    $('.sectionContent').each(function() {
        var h = $(this).height();
        $(this).height(h).addClass('noHeight');
    });
    $('.sectionHeader').click(function() {
        $(this).next('.sectionContent').toggleClass('noHeight');
    });
});

For completeness, the relevant css classes:

.sectionContent {
    overflow: hidden;
    -webkit-transition: all 0.3s ease-in;
    -moz-transition: all 0.3s ease-in;
    -o-transition: all 0.3s ease-in;
    transition: all 0.3s ease-in;
}
.noHeight {
        height: 0px !important;
}

Now the height transitions work without any delays.

Sibling answered 5/9, 2013 at 12:52 Comment(2)
Works like a charm, though I did notice that the height doesn't update properly if the window is resized enough to squeeze elements below the overflow-hidden container. To get around this I put the accordian contents in another div without overflow-hidden, set that div's height on page load, and then put a resize handler on the page that makes sure the accordian's height always matches the inner div. Works beautifully!Readymade
From what I can tell, this solution only works when the content has a fixed height (100px/100em/etc) but no animation will occur if content has height auto, unset, 100%, or anything like thatPennsylvanian
T
6

There is a new pure CSS solution that solves all the issues that other CSS solutions had. You can use grid-template-rows: 0fr; for collapsed and 1fr; for expanded. Look at the code for details:

/* theme */

.panel-expand {
  background-color: black;
  color: white;
  padding: 20px;
  width: 400px;
  transition: all 200ms ease-in-out;
}


/* mechanism */

.panel-expand {
  display: grid;
  grid-template-rows: 0fr;
}

.panel-expand:hover {
    grid-template-rows: 1fr;
}

.panel-expand__content {
  overflow: hidden;
}
<div class="panel-expand">
  <div class="panel-expand__content">
    <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Modi corrupti, sint distinctio deserunt vel unde consequatur sequi nobis necessitatibus quis ad officiis doloremque ab, blanditiis facere possimus obcaecati voluptate sed!</p>
    <p>Voluptas, ipsa porro? Ex nostrum culpa magnam magni officiis laboriosam fugit molestias amet reiciendis sunt facere, debitis odit neque totam natus ad aliquam minus, sed repellat eveniet, et impedit ipsa.</p>
  </div>
</div>

I found it mentioned in Kevin Powell video https://www.youtube.com/watch?v=B_n4YONte5A

Tetradymite answered 1/8, 2023 at 7:36 Comment(3)
This is fine if the height of the grid itself doesn't change, but if you need to expand the grid obviously it won't work.Tensiometer
Nice ! That is very usefull.Barstow
This is hilarious, and amazingApocrine
B
2

In case anyone is reading this, I have not found a solution and went with an expand-only effect (which was achieved by moving the transition style to the expanded class definition)

Bioecology answered 17/4, 2013 at 8:14 Comment(2)
would you mind posting the solution in a JS Fiddle or similar?Congresswoman
It's 2016 and it still doesn't have a solution. It's a shame.Jefferson
S
1

Use display:flex. This will work:

.parent > div {
  display: flex;
  flex-direction: column;
  height: 0px;
  max-height: 0px;
  opacity: 0;
  overflow: hidden;
  transition: all 0.3s;
}

.parent > div.active {
  opacity: 1; 
  height: 100%;
  max-height: none; /* important for animation */
}
Stoma answered 26/10, 2020 at 11:36 Comment(3)
did not work for meRansack
Me neither. It doesn't seem to transition at all, just snap.Lashing
it works only for paddings of the active element.Tartan
B
0

The solution is actually quite simple. Make a child div, that has the content. The parent div will be the one that expands collapses.

On load the parent div will have a max-height. when toggling, you can check the child height by writing document.querySelector('.expand-collapse-inner').clientHeight; and set the maxheight with javascript.

In your CSS, you will have this

.parent {
transition: max-height 250ms;
}
Bushelman answered 13/11, 2018 at 11:42 Comment(0)
D
-3

You can accomplish this just fine using jQuery Transit:

$(function () {
    $(".paragraph").click(function () {
        var expanded = $(this).is(".expanded");
        if (expanded) 
        {
            $(this).transition({ 'max-height': '4em', overflow: 'hidden' }, 500, 'in', function () {$(this).removeClass("expanded"); });
        } 
        else 
        {
            $(this).transition({ 'max-height': $(this).get(0).scrollHeight, overflow: ''}, 500, 'out', function () { $(this).addClass("expanded"); });
        }
    });
});

You can definitely tidy it up a bit to your liking, but that should do what you want.

JS Fiddle Demo

Dogmatism answered 19/4, 2013 at 12:50 Comment(1)
thanks, I know of ways to do it with javascript, I wanted a way to do it by only changing the classnameBioecology

© 2022 - 2024 — McMap. All rights reserved.