Search

SlideDown Animation Jump Revisited

Posted on 15th April 2009 — When using slideDown depending on the layout of your page, you could still see the jumping effect, regardless of whether you fix the padding around the element.

Watch

Watch Animation Jump Revisited screencast (Alternative flash version)

QuickTime version is approximately 20Mb, flash version is streaming.

View the demo used in the screencast

Understanding the Problem

Similarly to before the animation would jump when it gets towards the end. However, this time the problem can’t be fixed by moving the padding around.

The problem is actually due to an incorrect height being determined, which is triggered by a number of factors. The element being animated…

  1. Doesn’t have a predefined width, and in the document it is inheriting the width from a parent element.
  2. Is changed to position: absolute and as such the margins around the top and bottom no longer collapse. This is to determine the height by bringing it back in to view (whiles having visibility: none set) but not affecting the flow of the page.

As you’ll see in the screencast, just setting a width doesn’t quite get the correct height.

So we can’t just use CSS to fix the jump, we need to write slightly different jQuery.

Fixing the Problem

The task requires us to:

  1. Grab and store the initial height before hiding the element
  2. Set an inline height to prevent the first reveal from jumping
  3. If we are revealing, and the element is visible, animate to zero height and then hide
  4. Otherwise, show and animate the height to the initial captured height
var $div = $('#test');
var height = $div.height();
$div.hide().css({ height : 0 });

$('a').click(function () {
  if ( $div.is(':visible') ) {
    $div.animate({ height: 0 }, { duration: 2500, complete: function () {
        $div.hide();
      } 
    });
  } else {
    $div.show().animate({ height : height }, { duration: 2500 });
  }
    
  return false;
});

Wrapping Up

I’m going to post a bug on the jQuery dev site, and I’ve had a play around the code to be able to accurately establish the height of a hidden element - it’s not easy.

Hopefully this might be one of the last times we run in to the jump problem!

Related screencasts

Demo

If you find this demo doesn't work as expected, it's possibly due to the demo running from within an iframe. Try running the demo in it's own window.

Source Code

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Animation Jump - version 2</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<style type="text/css" media="screen">
body { background-color: #fff; font: 16px Helvetica, Arial; color: #000; font-size: 16px; line-height: 20px; padding: 20px; }
a { line-height: 40px; }
#container { width: 400px; }
#test { background: #c00; color: #fff; }
#test p { margin: 0; margin-bottom: 16px; }
</style>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript" charset="utf-8">
$(document).ready(function () {
    var $div = $('#test');
    var height = $div.height();
    $div.hide().css({ height : 0 });

    $('a').click(function () {
        if ($div.is(':visible')) {
            $div.animate({ height: 0 }, { duration: 2500, complete: function () {
                $div.hide();
            } });
        } else {
            $div.show().animate({ height : height }, { duration: 2500 });
        }
        
        return false;
    });
});
</script>
</head>
<body>
  <div><a href="#test">reveal</a></div>

  <div id="container">
    <div id="test">
      <p>Hello from JS Bin</p>
      <p id="hello">this is another content block - but it's going to jump when it's exposed :-(</p>
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
    </div>
  </div>

</body>
</html>

Comments

  1. Chris Greenhough On 15th April 2009 at 18:04

    This is one of those horrible gotchas that can make us non-developers feel like such a chump. I’ve had similar issues with actionscript where doing something that isn’t obviously wrong gives unexpected or inconsistent results and is an absolute brain-ache to resolve.

    As ever, Remy, your calm analytical approach saves the day. Are you like that in real life or only when recording a screencast? :-)

  2. Jay Welsh On 15th April 2009 at 19:04

    Hi Remy, thanks for that I was having a similar problem.

    Would it be possible for you to create a screen cast on how to recreate Apple’s online audio player, you can find it at: http://www.apple.com/ipodshuffle/voiceover.html

    When you click on the play button it shows you that the audio file is loading and then displays the progress of the audio on a ring around the player, its very cool.

    I had a look at the source code but it requires Scriptalicious, Prototype and many more JavaScript files. I hope you will be able to create this using your great jQuery skills.

    Thanks in advance Remy.

  3. Malen On 15th April 2009 at 23:04

    Remy - could the .height() jquery call not set the first and last child margins to 0 when it also sets the position to absolute in order to calculate the height - reverting the margins back afterwards?

  4. Remy On 16th April 2009 at 08:04

    @Chris - I can but only try :-)

    @Malen - that’s exactly the approach I began with to try to patch jQuery for a fix. However, as you’ll see in the screencast, if you’re inheriting the width from a parent element, when you set the position to absolute, the width expands to the width of the first parent with position: relative or absolute - which often we won’t set in our CSS since we’re not expecting this behind the scenes.

    So the next step of my patch was to grab the first visible parent’s width and swap it on the element in question when trying to determine the height, but I’m concerned about the performance, and hadn’t tested it completely.

  5. Chris Greenhough On 16th April 2009 at 09:04

    And I thought inheritance was meant to help… Seems not.

  6. Anica On 20th April 2009 at 13:04

    How do you use this, if you want more “jump” divs? Or is it not possible to use this with more divs?

  7. mark On 22nd April 2009 at 17:04

    What would I have to do to get multiple copies of this onto one page? And have them open individually, as at the moment if you click one, they all open? thanks

  8. author On 24th April 2009 at 20:04

    thanks for posting this. Your tutorials are awesome too. keep up the good work, it’s appreciated!

  9. Thomas Kadlec On 25th April 2009 at 00:04

    Love all your screencasts Remy! It’s a great way to learn jQuery. I have sort of an odd quesiton… in the jQuery community is there a common single syllable vocalization for the ‘$’ (dollar sign) when reading jQuery out loud? for example, programmers commonly say ‘dot’ for period, ‘bang’ for exclamation point, ‘hash’ for the number symbol. Is there one for $ as well?

  10. Mike Taylor On 27th April 2009 at 16:04

    I’ve been really struggling with this lately, and while this helps a lot, it does rely on having a single element with a known ID, and a single variable to store the height. I need a way to fix this where there are an unknown number of elements to slide/toggle. I can use a selector to add the onclick to each one, but how can I store the height of all of them?

    I currently have this:

    $(".btnToggle[rel]").click(function() {   
        var show = $(this).attr("rel");
        $("#" + show).slideToggle("def", "easeOutQuad");
        $(this).toggleClass("selected");
        return false;
    });
    

    Any thoughts?

    Thanks,

    Mike

  11. william On 27th April 2009 at 19:04

    Code doesn’t seem to work with multiple items? Any work around?

  12. Mike Taylor On 28th April 2009 at 08:04

    I think I’ve got it; this seems to work:

    $(".btnToggle[rel]").each(
    function(){
    
        var show = $(this).attr("rel");
        var $div = $('#' + show);
        var height = $div.height();
        $div.hide().css({ height : 0 });
    
        $(this).bind (
            "click",
            function(){
                if ($div.is(':visible')) {
                    $(this).removeClass("selected");
                    $div.animate({ height: 0 }, { duration: 500, easing: "easeOutQuad", complete: function () {
                        $div.hide();
                    } });
                } else {
                    $(this).addClass("selected");
                    $div.show().animate({ height : height }, { duration: 500, easing: "easeOutQuad" });
                }
                return false;
            }
        );
    });
    
  13. mecaniqueorange On 28th April 2009 at 19:04

    new theme, i like it :)

  14. Reece Conrad On 30th April 2009 at 19:04

    Love seeing more people who are picky about their animations. One problem, however:

    In FF3 (didn’t test others), if I double-click the link while it’s sliding up, it breaks the code and it won’t run the animation any more. I know this would rarely be an issue, but I’m just wondering why it happens and how we could fix it.

  15. Chris Greenhough On 3rd May 2009 at 15:05

    @Thomas… Bang for exclamation mark - I love it! I’m pretty sure dollar is just dollar, though… Remy? In (pre-web) typography we used to colloquially refer to an exclamation mark as a screamer, but maybe that was just the studio I worked in!

  16. jQuery Howto On 4th May 2009 at 11:05

    Thanks for the tip. Those who read the article also should read the previous article “Animation Jump tip” and “Font cleartype problems with fadeIn() and fadeOut()“.

  17. Contorra On 4th May 2009 at 22:05

    Thanx a lot, great site! We are using jQuery with Drupal a lot. And will use more!

  18. Henrik Bechmann On 7th May 2009 at 16:05

    You can also deal with the jump issue by giving an appropriate enclosing div the css rule:

    position:relative

    This way the hidden calculation (position: absolute) happens within the same bounds as the visible element.

  19. Oleg On 8th May 2009 at 06:05

    Also what should work, is making your div include the paragraph’s margin by using {position: relative} and to really get it across all browsers with {float: left; clear: none} and a clear div at the end. Something like this: $content

    Of course you shouldn’t use inline styles, I did for the sake of the example.

    If your coding is proper you won’t need to load up your page with some much JS, the functionality to read height is already build-into jQuery there’s no need to simulate it again.

    Anyway that’s my take on it :)

  20. ugg boots for less On 14th May 2009 at 19:05

    Oh! Great job.I like your every post.

    Really wonderful piece of information and I appreciate it that you share something so useful with the readers of this blog.

  21. Zach Harkey On 20th May 2009 at 16:05

    @Thomas $ = Buck

  22. parsifal On 20th May 2009 at 18:05

    Probably you get this all the time, but THIS is exactly what I needed. Thank You.

  23. Paul Irish On 28th May 2009 at 00:05

    @Thomas - $ is typically called ‘dollar’ when reading code, but it’s also known as the ‘bling’ function. ;-)

  24. DJ On 7th June 2009 at 03:06

    Hello … the page is completely broken and almost unreadable in my firefox 2.0.10 - actually it was working for months and on the slidedown animation jump post - but now with the last post it’s completely broken and I can’t tell where I’m posting. It’s been that way for several days now, so I thought I’d mention it.

  25. Dj On 10th June 2009 at 18:06

    FYI… site is back up on my firefox today, you must have done something (at least I hope it was you). I’ve been trying it every day on both my IE6 and Firefox. It’s been working on IE (go figure) but not on firefox. Today both are back up? Any explanations?

Comments are now closed.