180

I want to animate a <div> from 200px to auto height. I can’t seem to make it work though. Does anyone know how?

Here’s the code:

$("div:first").click(function(){
  $("#first").animate({
    height: "auto"
  }, 1000 );
});
Sebastian Simon
  • 18,263
  • 7
  • 55
  • 75
Daniel
  • 1,869
  • 2
  • 12
  • 7

21 Answers21

268
  1. Save the current height:

    var curHeight = $('#first').height();
    
  2. Temporarily switch the height to auto:

    $('#first').css('height', 'auto');
    
  3. Get the auto height:

    var autoHeight = $('#first').height();
    
  4. Switch back to curHeight and animate to autoHeight:

    $('#first').height(curHeight).animate({height: autoHeight}, 1000);
    

And together:

var el = $('#first'),
    curHeight = el.height(),
    autoHeight = el.css('height', 'auto').height();
el.height(curHeight).animate({height: autoHeight}, 1000);
David Tang
  • 92,262
  • 30
  • 167
  • 149
  • @Daniel, where is your JS code? Post that bit, and also parts of the HTML that show the elements you refer to. – David Tang Feb 15 '11 at 12:56
  • 21
    This works, but I added a callback that restores the auto-grow behavior to the element `.animated({height: autoHeight}, 1000, function(){ el.height('auto'); });` – rg89 Nov 26 '12 at 21:56
  • Becareful about setting fixed heights on responsive designs. It turns into a mess if the user resizes the screen. Best to set height to 'auto' once the animation is complete. – Jonathan Tonge Jan 27 '14 at 14:59
  • If you don't start with a fixed height of '200px' like the OP, you can set the element to '100%' with CSS and it works as well. – PHearst Oct 21 '14 at 11:56
  • 4
    This has the potential to cause FOUC. The user might see the element jump to full height for a split second before animating. – Dingredient Dec 17 '14 at 00:48
  • temporary switching to auto height is really visible for a couple of frames – FiftyStars Oct 05 '15 at 09:24
  • 1
    You can prevent the FOUC ("flash of unstyled content") by initially giving the element `opacity: 0; position: absolute;` while measuring it and removing those once you're done. – JacobEvelyn Apr 14 '16 at 14:59
205

IMO this is the cleanest and easiest solution:

$("#first").animate({height: $("#first").get(0).scrollHeight}, 1000 );

Explanation: The DOM already knows from its initial rendering what size the expanded div will have when set to auto height. This property is stored in the DOM node as scrollHeight. We just have to fetch the DOM Element from the jQuery Element by calling get(0) and then we can access the property.

Adding a callback function to set the height to auto allows for greater responsiveness once the animation is complete (credit chris-williams):

$('#first').animate({
    height: $('#first').get(0).scrollHeight
}, 1000, function(){
    $(this).height('auto');
});
Community
  • 1
  • 1
Liquinaut
  • 3,759
  • 1
  • 21
  • 17
  • 2
    Amazing! According to https://developer.mozilla.org/en-US/docs/Web/API/Element.scrollHeight it's even supported in IE8, compared to `clientHeight`, which seems to be unsupported: https://developer.mozilla.org/en-US/docs/Web/API/Element.clientHeight – Sven Feb 08 '15 at 22:17
  • this doesn't seem to take margins into account – Jack James Mar 01 '15 at 15:43
  • 1
    Margin is by definition of the box model not part of the height of an object. You could always add the margin yourself, though. – Liquinaut May 21 '15 at 19:13
  • I was having problems with the accepted answer. Sometimes (but not always), it would get the same value for the current height and the auto height. This answer works perfectly for me. I am surprised I have never seen the `scrollHeight` property before. – bytesized Jun 30 '15 at 22:12
  • 23
    This should be the accepted answer as it works best without any flickering and really does the job well – Einius Jul 09 '15 at 07:18
  • 7
    I also think this is the best solution. I would add to it a callback function to set height to auto for more responsiveness. `$('#first').animate({ height: $('#first').get(0).scrollHeight }, 1000, function() { $(this).height('auto'); });` – Chris Williams Oct 21 '15 at 10:29
  • 1
    Wow, this is super elegant. It also works with `scrollWidth` for width animations. – nils Nov 30 '15 at 10:35
  • Fantastic solution. Deserves the accept. Also works when the window is resized whereas the other doesn't unless you write a function to modify the initial heights and redo all the calculations. – tomarrell Dec 09 '15 at 22:31
24

This is basically the same approach as the answer by Box9 but I wrapped it in a nice jquery plugin that takes the same arguments as a regular animate, for when you need to have more animated parameters and get tired of repeating the same code over and over:

;(function($)
{
  $.fn.animateToAutoHeight = function(){
  var curHeight = this.css('height'),
      height = this.css('height','auto').height(),
      duration = 200,
      easing = 'swing',
      callback = $.noop,
      parameters = { height: height };
  this.css('height', curHeight);
  for (var i in arguments) {
    switch (typeof arguments[i]) {
      case 'object':
        parameters = arguments[i];
        parameters.height = height;
        break;
      case 'string':
        if (arguments[i] == 'slow' || arguments[i] == 'fast') duration = arguments[i];
        else easing = arguments[i];
        break;
      case 'number': duration = arguments[i]; break;
      case 'function': callback = arguments[i]; break;
    }
  }
  this.animate(parameters, duration, easing, function() {
    $(this).css('height', 'auto');
    callback.call(this, arguments);
  });
  return this;
  }
})(jQuery);

edit: chainable and cleaner now

Ghost-Man
  • 2,179
  • 1
  • 21
  • 25
w0ps
  • 249
  • 4
  • 5
23

A better solution would not rely on JS to set the height of your element. The following is a solution that animates a fixed height element to full ("auto") height:

var $selector = $('div');
    $selector
        .data('oHeight',$selector.height())
        .css('height','auto')
        .data('nHeight',$selector.height())
        .height($selector.data('oHeight'))
        .animate({height: $selector.data('nHeight')},400);

https://gist.github.com/2023150

Tim Hettler
  • 1,246
  • 9
  • 12
  • 2
    This oneliner is not easy to understand, maybe writing several lines would help others a little better. – Jaap Dec 04 '12 at 13:37
  • This is the best solution because the auto height may change if the user adjusts the window size. See the following: //animates the height of the filters function toggleSlider(){ if ($('#filters').height() != 0) { $('#filters').animate({height:'0'}); } else{ var $selector = $('#filters'); $selector .data('oHeight',$selector.height()) .css('height','auto') .data('nHeight',$selector.height()) .height($selector.data('oHeight')) .animate({height: $selector.data('nHeight')},400); }; console.log('agg'); } – Ricky Sahu May 25 '14 at 17:10
  • Works to make the div open up, but does not animate over 400ms. Maybe I have something else set differently, but it just opens in a blink. – ntgCleaner Mar 11 '16 at 14:54
  • Works but this sets `height` to a fixed value (e.g. 122px). My element changed height after a while, so I had to replace the duration argument (400) with options `{duration: 400, complete: function() {$selector.css('height', 'auto');}}` – jsruok Jun 27 '17 at 06:20
12

this is working and it is simplier then solutions before:

CSS:

#container{
  height:143px;  
}

.max{
  height: auto;
  min-height: 143px;
}

JS:

$(document).ready(function() {
    $("#container").click(function() {      
        if($(this).hasClass("max")) {
            $(this).removeClass("max");
        } else {
            $(this).addClass("max");
        }

    })
});

Note: This solution requires jQuery UI

czLukasss
  • 730
  • 8
  • 16
10
var h = document.getElementById('First').scrollHeight;
$('#First').animate({ height : h+'px' },300);
Juan Mellado
  • 14,973
  • 5
  • 47
  • 54
7

You can always wrap the child elements of #first and save height height of the wrapper as a variable. This might not be the prettiest or most efficient answer, but it does the trick.

Here's a fiddle where I included a reset.

but for your purposes, here's the meat & potatoes:

$(function(){
//wrap everything inside #first
$('#first').children().wrapAll('<div class="wrapper"></div>');
//get the height of the wrapper 
var expandedHeight = $('.wrapper').height();
//get the height of first (set to 200px however you choose)
var collapsedHeight = $('#first').height();
//when you click the element of your choice (a button in my case) #first will animate to height auto
$('button').click(function(){
    $("#first").animate({
        height: expandedHeight            
    })
});
});​
kapa
  • 77,694
  • 21
  • 158
  • 175
Usha
  • 156
  • 2
  • 5
5

Use slideDown and slideUp

$("div:first").click(function(){ $("#first").slideDown(1000); });
Ronny Sherer
  • 8,349
  • 1
  • 22
  • 9
  • 1
    This doesn't solve the height:auto function since slideUp will completely collapse the div. – Jaap Dec 04 '12 at 13:31
5

I managed to fix it :D heres the code.

var divh = document.getElementById('first').offsetHeight;
$("#first").css('height', '100px');
$("div:first").click(function() {
  $("#first").animate({
    height: divh
  }, 1000);
});
Jaap
  • 3,081
  • 2
  • 29
  • 50
Daniel
  • 1,869
  • 2
  • 12
  • 7
4

You can make Liquinaut's answer responsive to window size changes by adding a callback that sets the height back to auto.

$("#first").animate({height: $("#first").get(0).scrollHeight}, 1000, function() {$("#first").css({height: "auto"});});
Cyl
  • 41
  • 2
4

Basically the height auto is only available for you after the element is rendered. If you set a fixed height, or if your element is not displayed you can't access it without any tricks.

Luckily there are some tricks you may use.

Clone the element, display it outside of the view give it height auto and you can take it from the clone and use it later for the main element. I use this function and seems to work well.

jQuery.fn.animateAuto = function(prop, speed, callback){
    var elem, height, width;

    return this.each(function(i, el){
        el = jQuery(el), elem =    el.clone().css({"height":"auto","width":"auto"}).appendTo("body");
        height = elem.css("height"),
        width = elem.css("width"),
        elem.remove();

        if(prop === "height")
            el.animate({"height":height}, speed, callback);
        else if(prop === "width")
            el.animate({"width":width}, speed, callback);  
        else if(prop === "both")
            el.animate({"width":width,"height":height}, speed, callback);
    });   
}

USAGE:

$(".animateHeight").bind("click", function(e){
    $(".test").animateAuto("height", 1000); 
});

$(".animateWidth").bind("click", function(e){
    $(".test").animateAuto("width", 1000);  
});

$(".animateBoth").bind("click", function(e){
    $(".test").animateAuto("both", 1000); 
});
  • 1
    If you don't want to use that function just do something like: var clone = element.clone() clone.appendTo('body') clone.css('height', 'auto') var itemHeight = clone.outerHeight(); clone.remove() now you have your item's height in itemHeight variable, so you can use it for more than just animations. – Stan George Aug 08 '16 at 14:14
3

you can always do this:

jQuery.fn.animateAuto = function(prop, speed, callback){
var elem, height, width;
return this.each(function(i, el){
    el = jQuery(el), elem = el.clone().css({"height":"auto","width":"auto"}).appendTo("body");
    height = elem.css("height"),
    width = elem.css("width"),
    elem.remove();

    if(prop === "height")
        el.animate({"height":height}, speed, callback);
    else if(prop === "width")
        el.animate({"width":width}, speed, callback);  
    else if(prop === "both")
        el.animate({"width":width,"height":height}, speed, callback);
});  
}

here is a fiddle: http://jsfiddle.net/Zuriel/faE9w/2/

Zuriel
  • 1,848
  • 1
  • 19
  • 32
3

Your selectors don't seem to match. Does your element have an ID of 'first', or is it the first element in every div?

A safer solution would be to use 'this':

// assuming the div you want to animate has an ID of first
$('#first').click(function() {
  $(this).animate({ height : 'auto' }, 1000);
});
EMMERICH
  • 3,424
  • 2
  • 18
  • 14
2

Here's one that works with BORDER-BOX ...

Hi guys. Here is a jQuery plugin I wrote to do the same, but also account for the height differences that will occur when you have box-sizing set to border-box.

I also included a "yShrinkOut" plugin that hides the element by shrinking it along the y-axis.


// -------------------------------------------------------------------
// Function to show an object by allowing it to grow to the given height value.
// -------------------------------------------------------------------
$.fn.yGrowIn = function (growTo, duration, whenComplete) {

    var f = whenComplete || function () { }, // default function is empty
        obj = this,
        h = growTo || 'calc', // default is to calculate height
        bbox = (obj.css('box-sizing') == 'border-box'), // check box-sizing
        d = duration || 200; // default duration is 200 ms

    obj.css('height', '0px').removeClass('hidden invisible');
    var padTop = 0 + parseInt(getComputedStyle(obj[0], null).paddingTop), // get the starting padding-top
        padBottom = 0 + parseInt(getComputedStyle(obj[0], null).paddingBottom), // get the starting padding-bottom
        padLeft = 0 + parseInt(getComputedStyle(obj[0], null).paddingLeft), // get the starting padding-left
        padRight = 0 + parseInt(getComputedStyle(obj[0], null).paddingRight); // get the starting padding-right
    obj.css('padding-top', '0px').css('padding-bottom', '0px'); // Set the padding to 0;

    // If no height was given, then calculate what the height should be.
    if(h=='calc'){ 
        var p = obj.css('position'); // get the starting object "position" style. 
        obj.css('opacity', '0'); // Set the opacity to 0 so the next actions aren't seen.
        var cssW = obj.css('width') || 'auto'; // get the CSS width if it exists.
        var w = parseInt(getComputedStyle(obj[0], null).width || 0) // calculate the computed inner-width with regard to box-sizing.
            + (!bbox ? parseInt((getComputedStyle(obj[0], null).borderRightWidth || 0)) : 0) // remove these values if using border-box.
            + (!bbox ? parseInt((getComputedStyle(obj[0], null).borderLeftWidth || 0)) : 0) // remove these values if using border-box.
            + (!bbox ? (padLeft + padRight) : 0); // remove these values if using border-box.
        obj.css('position', 'fixed'); // remove the object from the flow of the document.
        obj.css('width', w); // make sure the width remains the same. This prevents content from throwing off the height.
        obj.css('height', 'auto'); // set the height to auto for calculation.
        h = parseInt(0); // calculate the auto-height
        h += obj[0].clientHeight // calculate the computed height with regard to box-sizing.
            + (bbox ? parseInt((getComputedStyle(obj[0], null).borderTopWidth || 0)) : 0) // add these values if using border-box.
            + (bbox ? parseInt((getComputedStyle(obj[0], null).borderBottomWidth || 0)) : 0) // add these values if using border-box.
            + (bbox ? (padTop + padBottom) : 0); // add these values if using border-box.
        obj.css('height', '0px').css('position', p).css('opacity','1'); // reset the height, position, and opacity.
    };

    // animate the box. 
    //  Note: the actual duration of the animation will change depending on the box-sizing.
    //      e.g., the duration will be shorter when using padding and borders in box-sizing because
    //      the animation thread is growing (or shrinking) all three components simultaneously.
    //      This can be avoided by retrieving the calculated "duration per pixel" based on the box-sizing type,
    //      but it really isn't worth the effort.
    obj.animate({ 'height': h, 'padding-top': padTop, 'padding-bottom': padBottom }, d, 'linear', (f)());
};

// -------------------------------------------------------------------
// Function to hide an object by shrinking its height to zero.
// -------------------------------------------------------------------
$.fn.yShrinkOut = function (d,whenComplete) {
    var f = whenComplete || function () { },
        obj = this,
        padTop = 0 + parseInt(getComputedStyle(obj[0], null).paddingTop),
        padBottom = 0 + parseInt(getComputedStyle(obj[0], null).paddingBottom),
        begHeight = 0 + parseInt(obj.css('height'));

    obj.animate({ 'height': '0px', 'padding-top': 0, 'padding-bottom': 0 }, d, 'linear', function () {
            obj.addClass('hidden')
                .css('height', 0)
                .css('padding-top', padTop)
                .css('padding-bottom', padBottom);
            (f)();
        });
};

Any of the parameters I used can be omitted or set to null in order to accept default values. The parameters I used:

  • growTo: If you want to override all the calculations and set the CSS height to which the object will grow, use this parameter.
  • duration: The duration of the animation (obviously).
  • whenComplete: A function to run when the animation is complete.
Ross Brasseaux
  • 3,879
  • 1
  • 28
  • 48
2

I needed this functionality for multiple read more area's on one page implementing this into a Wordpress shortcode I ran into the same problem.

Design technically all of the read more span's on the page have a fixed height. And I wanted to be able to expand them separately to an auto height with a toggle. First click: 'expand to full height of text span', second click: 'collapse back to default height of 70px'

Html

 <span class="read-more" data-base="70" data-height="null">
     /* Lots of text determining the height of this span */
 </span>
 <button data-target='read-more'>Read more</button>

CSS

span.read-more {
    position:relative;
    display:block;
    overflow:hidden;
}

So above this looks very simple the data-base attribute I need to set the fixed height needed. The data-height attribute I used to store the actual (dynamic) height of the element.

The jQuery part

jQuery(document).ready(function($){

  $.fn.clickToggle = function(func1, func2) {
      var funcs = [func1, func2];
      this.data('toggleclicked', 0);
      this.click(function() {
          var data = $(this).data();
          var tc = data.toggleclicked;
          $.proxy(funcs[tc], this)();
          data.toggleclicked = (tc + 1) % 2;
      });
      return this;
  };

    function setAttr_height(key) {
        $(key).each(function(){
            var setNormalHeight = $(this).height();
            $(this).attr('data-height', setNormalHeight);
            $(this).css('height', $(this).attr('data-base') + 'px' );
        });
    }
    setAttr_height('.read-more');

    $('[data-target]').clickToggle(function(){
        $(this).prev().animate({height: $(this).prev().attr('data-height')}, 200);
    }, function(){
        $(this).prev().animate({height: $(this).prev().attr('data-base')}, 200);
    });

});

First I've used a clickToggle function for my first and second click. The second function is more important: setAttr_height() All of the .read-more elements have their actual heights set on page load in the base-height attribute. After that the base height is set through the jquery css function.

With both of our attributes set we now can toggle between them in a smooth way. Only chang the data-base to your desired (fixed)height and switch the .read-more class for your own ID

You can all see it working in a fiddle FIDDLE

No jQuery UI needed

Paul
  • 633
  • 2
  • 13
  • 22
2

Toggle slide (Box9's answer expanded)

$("#click-me").click(function() {
  var el = $('#first'),
  curHeight = el.height(),
  autoHeight = el.css('height', 'auto').height(),
  finHeight = $('#first').data('click') == 1 ? "20px" : autoHeight;
  $('#first').data('click', $(this).data('click') == 1 ? false : true);
  el.height(curHeight).animate({height: finHeight});
});
#first {width: 100%;height: 20px;overflow:hidden;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="first">
  <div id="click-me">Lorem ipsum dolor sit amet, consectetur adipiscing elit</div>
  Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit,
</div>
Community
  • 1
  • 1
Cedric Ipkiss
  • 5,662
  • 2
  • 43
  • 72
2

Try this one ,

var height;
$(document).ready(function(){
    $('#first').css('height','auto');
    height = $('#first').height();
    $('#first').css('height','200px');
})

 $("div:first").click(function(){
  $("#first").animate({
    height: height
  }, 1000 );
});
Prakash
  • 6,562
  • 3
  • 25
  • 33
  • this is not gonna work your var height is just accessible inside the ready function. – meo Feb 15 '11 at 12:12
  • define the height before the ready function, and use only height than var height.. this way it may work daniel – Prakash Feb 15 '11 at 15:47
2

If all you are wanting is to show and hide say a div, then this code will let you use jQuery animate. You can have jQuery animate the majority of the height you wish or you can trick animate by animating to 0px. jQuery just needs a height set by jQuery to convert it to auto. So the .animate adds the style="" to the element that .css(height:auto) converts.

The cleanest way I have seen this work is to animate to around the height you expect, then let it set auto and it can look very seamless when done right. You can even animate past what you expect and it will snap back. Animating to 0px at a duration of 0 just simply drops the element height to its auto height. To the human eye, it looks animated anyway. Enjoy..

    jQuery("div").animate({
         height: "0px"/*or height of your choice*/
    }, {
         duration: 0,/*or speed of your choice*/
         queue: false, 
         specialEasing: {
             height: "easeInCirc"
        },
         complete: function() {
             jQuery(this).css({height:"auto"});
        }
    });

Sorry I know this is an old post, but I felt this would be relevant to users seeking this functionality still with jQuery who come across this post.

designdrumm
  • 64
  • 1
  • 4
1

I am posting this answer even though this thread is old. I couldn't get the accepted answer to work for me. This one works well and is pretty simple.

I load the height of each div I want into the data

$('div').each(function(){
    $(this).data('height',$(this).css('height'));
    $(this).css('height','20px');
});

Then I just use that when animating on click.

$('div').click(function(){
    $(this).css('height',$(this).data('height'));
});

I am using CSS transition, so I don't use the jQuery animate, but you could do animate just the same.

Leeish
  • 5,203
  • 2
  • 17
  • 45
1

you can store it in a data attribute.

$('.colapsable').each(function(){
    $(this).attr('data-oheight',$(this).height());
    $(this).height(100);
});

$('.colapsable h2:first-child').click(function(){
    $(this).parent('.colapsable').animate({
            height: $(this).parent('.colapsible').data('oheight')
        },500);
    }
});
1

I put together something that does exactly what I was looking for and looks great. Using the scrollHeight of an element gets you the height of when it was loaded in the DOM.

 var clickers = document.querySelectorAll('.clicker');
    clickers.forEach(clicker => {
        clicker.addEventListener('click', function (e) {
            var node = e.target.parentNode.childNodes[5];
            if (node.style.height == "0px" || node.style.height == "") {
                $(node).animate({ height: node.scrollHeight });
            }
            else {
                $(node).animate({ height: 0 });
            }
        });
    });
.answer{
        font-size:15px;
        color:blue;
        height:0px;
        overflow:hidden;
       
    }
 <div class="row" style="padding-top:20px;">
                <div class="row" style="border-color:black;border-style:solid;border-radius:4px;border-width:4px;">
                    <h1>This is an animation tester?</h1>
                    <span class="clicker">click me</span>
                    <p class="answer">
                        I will be using this to display FAQ's on a website and figure you would like this.  The javascript will allow this to work on all of the FAQ divs made by my razor code.  the Scrollheight is the height of the answer element on the DOM load.  Happy Coding :)
                         Lorem ipsum dolor sit amet, mea an quis vidit autem. No mea vide inani efficiantur, mollis admodum accusata id has, eam dolore nemore eu. Mutat partiendo ea usu, pri duis vulputate eu. Vis mazim noluisse oportere id. Cum porro labore in, est accumsan euripidis scripserit ei. Albucius scaevola elaboraret usu eu. Ad sed vivendo persecuti, harum movet instructior eam ei.
                    </p>
                </div>
            </div>
            <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
JimmyTwoShoes
  • 51
  • 1
  • 7