views:

42

answers:

3

I'm using $('#container_div').load(url) to populate a div via ajax. I would like to animate the height to the height of the returned content but really cant figure out how to achieve this.

I've tried using something like this:

$('#main').fadeOut(function() {

 $('#main').load(url, function(data) {
     var newHeight = $(data).height();
        $('#main').animate({height:newHeight}, function() {$('#main').fadeIn();});
     });
 });

But can see that this is wrong on so many levels. Especially due to the fact that newHeight === undefined.

Can anybody point me in the right direction here? I would be eternally grateful.

+1  A: 

It's because $(data) isn't in the DOM, however $(this) is :)

$('#main').fadeOut(function() {

  $('#main').load(url, function(data) {
     var newHeight = $(this).height();
     $(this).animate({height:newHeight}, function() {$(this).fadeIn();});
  });
});

However the new height will already be what it's doing to be at this point, and you can't guess the height of the new content because it's all determined by it's container, you may be better off storing and restoring it, something like this:

$('#main').animate({ opacity: 0 }, function() {
  var height = $(this).height();        //previous height
  $('#main').load(url, function(data) {
     var newHeight = $(this).height();  //new height
     $(this).height(height)             //set old height before animating to new
            .animate({height:newHeight})//animate to new height
            .fadeIn();                  //fade is a queued animation
  });
});

I'm animating the opacity here so the display:none doesn't get applies, making this much simpler overall.

Nick Craver
I think OP wants the height of the new data instead of the `#main` element.
patrick dw
@patrick - They'll be the same since that's what's being loaded...but I added a method on for how to do what I *think* he's after :)
Nick Craver
But how do you know they're the same? Maybe the `#main` has a height of `0` with `overflow:hidden`, and wants it to animate to the natural height of the newly loaded content. (Or am I missing something obvious? Happens daily!) EDIT: Of course, the `fadeIn()` would make that unlikely! There's the obvious part. :o)
patrick dw
@patrick - They're internally the same, I'm *assuming* he doesn't have `overflow: hidden` you're correct, but seeing as he wants to expand to accommodate the content, I'm guessing it's not :) If it's overflow hidden, why animate at all? :)
Nick Craver
@patrick: then he'll have to change that style. The "natural" height is dependent on styles, page size, surrounding DOM... There's no fixed, universal size for a HTML fragment.
Shog9
@Nick: since you're starting with fadeOut, the rest of the animation (up 'till fadeIn()) is sorta pointless - the element will be styled `display:none`, have no height, etc.
Shog9
Nick, @Shog9 - Thank you for your comments. I'm getting it now. :o)
patrick dw
@Shog9 - If you're say loading a new section in though, it'll push the rest of the content down, or the border will expand, etc. You're right about the display though, I'll add to the style rules list to update. Edit: changed it to a much more simplified version where `display` doesn't come into play :)
Nick Craver
Thank you for all of this guys, it's very much appreciated! My understanding of all of this is a lot clearer now. :)
@Nick: you're still animating the height while the opacity is at 0 - so the effect is wasted. Also, fadeIn() doesn't appear to pair well with hiding via opacity: 0.
Shog9
@Shog - You're *assuming* it's wasted, if this element had one wrapping it for a border or content beneath it'd still occupy space, it's *that* I'm animating here, animating the element itself wouldn't make much sense.
Nick Craver
@Nick: I see what you're saying - the content itself won't animate, but the page layout will. Fair enough!
Shog9
A: 

First, load your data into a temporary div, which you have hidden way to the left and the top of the page with absolute positioning. You can then get the height of that (as long at it's height in the css is set to 'auto'. Use that height information to animate the div you are showing. Finally, don't forget to remove the temporary div from the dom and set the height of the div you show to 'auto' once the animation is complete.

Gus
A: 

Since fadeOut() finishes by hiding the target elements, chances are your #main will be completely hidden by the time your new data is loaded, rendering any animation of height invisible, and therefore pointless.

But you could just use something like $('#main').show(400) which will animate the element from a size of (0,0) and opacity of 0 to whatever size is allowed by the container and contents and a fully-visible opacity of 1 (and run these animations in parallel, making both of them visible).

But assuming you do care more about animating the height than you do about fading, you still have a problem: by the time load() calls its callback, the height of the target element(s) will already be the height of the content (or as close as possible to it). So animating won't do anything.

I posted a plugin on a previous question that will do what you want, but you'll need to use $.get() instead of load():

$.get(url, function(data) {
  $('#main').showHtml(data);
});

...where showHtml is defined as:

// Animates the dimensional changes resulting from altering element contents
// Usage examples: 
//    $("#myElement").showHtml("new HTML contents");
//    $("div").showHtml("new HTML contents", 400);
//    $(".className").showHtml("new HTML contents", 400, 
//                    function() {/* on completion */});
(function($)
{
   $.fn.showHtml = function(html, speed, callback)
   {
      return this.each(function()
      {
         // The element to be modified
         var el = $(this);

         // Preserve the original values of width and height - they'll need 
         // to be modified during the animation, but can be restored once
         // the animation has completed.
         var finish = {width: this.style.width, height: this.style.height};

         // The original width and height represented as pixel values.
         // These will only be the same as `finish` if this element had its
         // dimensions specified explicitly and in pixels. Of course, if that 
         // was done then this entire routine is pointless, as the dimensions 
         // won't change when the content is changed.
         var cur = {width: el.width()+'px', height: el.height()+'px'};

         // Modify the element's contents. Element will resize.
         el.html(html);

         // Capture the final dimensions of the element 
         // (with initial style settings still in effect)
         var next = {width: el.width()+'px', height: el.height()+'px'};

         el .css(cur) // restore initial dimensions
            .animate(next, speed, function()  // animate to final dimensions
            {
               el.css(finish); // restore initial style settings
               if ( $.isFunction(callback) ) callback();
            });
      });
   };


})(jQuery);
Shog9
This works beautifully, thank you so much :)