Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at cf.Objective() 2009 (Minneapolis, MN) with:

Use jQuery's SlideDown() With Fixed-Width Elements To Prevent Jumping

By Ben Nadel on

From what I have seen, the slideDown() method is perhaps one of the most used methods in jQuery. While the Sizzle library and the general jQuery API have revolutionized DOM access and manipulation, the slideDown() method has, in one stroke, allowed programmers to create a richer, more natural experience for their end users. Where user interfaces (UIs) used to feel mechanical, effects like the slideDown() function have helped them to feel "touchable" and user friendly. When you don't know how the the slideDown() method works, however, you can end up creating jumpy, jerky, and unexpected behavior.


 
 
 

 
  
 
 
 

No doubt, you've seen and maybe even coded a slideDown() effect where the final step of the animation was disproportionally large when compared to the smooth arch of the initial animation. This final "jump" in the exposure of the hidden content is due to a difference in height between what jQuery thinks the content will be and what it actually ends up requiring.

This delta in requirements is the result of the way in which jQuery calculates the height of the final, exposed content. Since hidden content has no offsets (height or width), jQuery has to actually make the content momentarily visible before it can calculate its height. And, in order to keep the user interface from jumping around, jQuery actually pulls the hidden content out of the document flow before making it "visible."

From what I have been told, it does this by temporarily changing the "position" of the hidden content to, "fixed." This new position behavior pulls the hidden content out of the document flow; however, the often-felt side-effect of this is that the hidden content is no longer constrained by the previous offset-parent (ie. the containing DOM node).

This difference in layout constraints is what causes the jumping in slideDown()-based animations. Once the hidden content has been pulled out of the document flow, its width may no longer be fixed. And, without a fixed width, we can easily end up with a different height.

To see what I'm talking about, take a look at the following code:

  • <!DOCTYPE html>
  • <html>
  • <head>
  • <title>Using jQuery SlideDown() With Fixed Width Elements</title>
  • </head>
  • <body>
  •  
  • <h1>
  • Using jQuery SlideDown() With Fixed Width Elements
  • </h1>
  •  
  •  
  • <!-- BEGIN: Container. -->
  • <div style="width: 300px ; border: 1px solid black ; padding: 10px ;">
  •  
  • Don't you want to
  • <a href="#" class="readMore">Read More</a>?
  •  
  •  
  • <!-- BEGIN: Hidden Content. -->
  • <div class="content" style="display: none ;">
  •  
  • This is where the additional content will go - the
  • content that the user can see when the Read More
  • link is clicked. We're going to use jQuery to slide
  • the content of this container into view, with a slow
  • transition. This is where the additional content will go
  • - the content that the user can see when the Read More
  • link is clicked. We're going to use jQuery to slide
  • the content of this container into view, with a slow
  • transition. This is where the additional content will go
  • - the content that the user can see when the Read More
  • link is clicked. We're going to use jQuery to slide
  • the content of this container into view, with a slow
  • transition. This is where the additional content will go
  • - the content that the user can see when the Read More
  • link is clicked. We're going to use jQuery to slide
  • the content of this container into view, with a slow
  • transition.
  •  
  • </div>
  • <!-- END: Hidden Content. -->
  •  
  •  
  • </div>
  • <!-- END: Container. -->
  •  
  •  
  • <script type="text/javascript" src="./jquery-1.6.3.js"></script>
  • <script type="text/javascript">
  •  
  •  
  • // Cache a reference to the hidden content.
  • var hiddenContent = $( "div.content" );
  •  
  • // Bind to the Read More link to toggle the
  • $( "a.readMore" ).click(
  • function( event ){
  •  
  • // Cancel the default event (this isn't a real link).
  • event.preventDefault();
  •  
  • // Check to see if the content is visible.
  • if (hiddenContent.is( ":visible" )){
  •  
  • // Hide it slowly.
  • hiddenContent.slideUp( 3000 );
  •  
  • } else {
  •  
  • // Show it slowly.
  • hiddenContent.slideDown( 3000 );
  •  
  • }
  •  
  • }
  • );
  •  
  •  
  • </script>
  •  
  • </body>
  • </html>

As you can see here, we have a container that has a fixed width of 300px. Inside this container, we then have a hidden content DIV that has no width constraints at all. And, if you watch the video above, you'll see an enormous jump in the slideDown() animation. This is due to the fact that height of the hidden content is extremely different based on the position of the hidden DIV. When the hidden content is not constrained to 300px - when it can spread across the entire browser window - it ends up taking significantly less vertical space:


 
 
 

 
 jQuery uses fixed-position elements to calculate height requirements in the slideDown() animation. 
 
 
 

In order to get rid of the jump at the end of the jQuery slideDown() animation, you have to make sure that the calculated height is the same as the final height. And, in order to do that, you have to make sure that the width of the hidden content is the same regardless of its position. The easiest way to do that is simply to add a fixed width to the hidden content.

  • <!-- BEGIN: Hidden Content. -->
  • <div class="content" style="display: none ; width: 300px ;">
  •  
  • This is where the additional content will go - the
  • content that the user can see when the Read More
  • link is clicked.....
  •  
  • </div>
  • <!-- END: Hidden Content. -->

Notice that our hidden DIV now has a fixed width of 300px - the same width it would be allocated if it was within the visible flow of the document. This will fix our animation completely.

jQuery's slideDown() method is awesome. But, in order to make sure that it always gives the same, rich, rewarding experience, it's important to understand how it works. And, once you do that, you can easily configure your code to remove all jumping or jerky motions from your slideDown() transitions.




Reader Comments

@Andrew,

Nice tip. I recently worked with jquery, and I think I had slidedown() as a part of it, but I do remember the container width coming into play at some point during manipulation. :-)

Reply to this Comment

@All,

Thanks guys. It's a problem that's bitten me a number of times. For a long time, I always just assumed it was jQuery messing up. Once someone told me (I wish I could remember who) how the height was calculated, it started to make much more sense where the jumping was coming from.

As a rule of thumb, I tend to try and "show/hide" containers that ONLY have a width (and sometimes height) on them. If I have borders and/or padding, I try to put those on an elements *within* the container being shown.

Reply to this Comment

@Ben, Thank you for the solution. If it happened with me, I would probably assume it was something with my computer messing up, like maybe not enough memory or something, as my computer messes up often and gives me all kinds of weird behavior. One thing that always got on my nerves when developing when first beginning was how when you develop a page and the page jumps around as it loads. Of course, that was WAY in the beginning. :-)

CSS has been a pain in my rear so far. If I could figure it out and become proficient with it, I am sure I would love it and it would lead to far more elegant solutions, and would probably make for better looking, designed, and coded sites. But front end development with UI's and what the user sees, and the whole aesthetic and design aspect of it is not my forte at all, and I can't tell you how much I have struggled with CSS. At one of my first development jobs, some of the design guys showed me how to use photoshop to design a site and cut it up, and during that time, table-less design was not all the rage, and it was still 100% acceptible and the standard of the industry to design sites using tables, so that is how I learned it, and that is what I learned. A dino like me is now having problems with this new trendy way of designing table-less web sites and using CSS. I know it is more elegant, better, etc., but it is very difficult to wrap your head around when you are used to doing the table stuff, and when that is how you learned it to begin with (and you aren't much of a design person at all). I continue to struggle with the CSS stuff.

Reply to this Comment

In the demo, you entered 300px as the width for the contained div, but the parent is 300px _and_ has a padding of 10px. So you probably want to enter 280px to account for the padding, don't you?

I'm guessing 300px worked because it gave the same number of lines but only as an accident.

Reply to this Comment

@Anna,

Yeah, CSS still feels very much like an art to me, not a science.

@Elisée,

Do to the box model, the 10px padding on the container should be *added* to the container's width. So, while it has an explicit width of 300px, its rendered width with padding and border should be 322px (300 + 10 + 10 + 1 + 1). The hidden content should then fit into the 300px width.... at least I think so. The box model is still something that throws me off :)

Reply to this Comment

@Ben, yeah, a lot of trial and error for me. An example is this box model stuff you are discussing with Elisee. I had no idea it worked that way. Good to know for future reference...

^ record shortest Anna comment ever.

Reply to this Comment

>> record shortest Anna comment ever.

Haha! Are you feeling ok? Might want to lie down for a bit until you recover. ;)

Ben: Just to confuse us all further, there's an alternative box model where you can specify the final width and the padding/etc is included within that size rather than appended to it. Not quite got full browser support yet, and I can't remember what it was called... flex or flux or similar.

If you're in a situation where you can't calculate width exactly (such as if padding is 1em or whatever), I suppose it would work to specify an approximate max-width value? So long as the value given is close enough, the final jump would be small enough not to be noticeable, right?

Reply to this Comment

@Peter,

yeah, I have a headache. Work has been extremely busy. But I am headed home now, thank goodness, and will be able to rest until tomorrow. :-) Can't quite tell you just how much this excites me. Sad, isn't it?

Reply to this Comment

Great! The video explanation and presentation made much easier to see and understand the problem and solution. Thanks Ben!

Reply to this Comment

I know CSS is the way to go, and tables not so much, but one thing that really did it for me with CSS was once when I was trying to do a table-less site, and the background WOULD NOT match up across the page. It woudln't fill in behind the content in one of my supposed "containers", thereby giving the bottom of my site an uneven background distribution. I had originally designed the thing using tables, but decided to try to be really smart and do the table-less thing, and I just simply could not get it to work. grrrrrr css. I had to revert back to tables. I would've had to completely change the design of the site otherwise.

Reply to this Comment

Great insight into a plaguing issue, thanks for that!

Now knowing the cause though, I can't help but wonder if it would be best for jQuery to take into consideration the width of the container of the content it's working with, seeing as how the way it's currently handled is a bit incomplete, it would appear.

Reply to this Comment

It seems like you can set the width automatically based on the parent before beginning the slide.

hiddenContent.width(hiddenContent.parent().width());

Reply to this Comment

@Nando,

Thank you so, so, so much for that! It's not that I don't want to do things "right"...I absolutely do! It's just that I fall short on ability when it comes to some things, and I hate, hate, hate, hate rigging and doing things "the wrong way", but sometimes I either get into a time crunch, or I reach a point to where it's just like hitting my head against a brick wall, and I really at that point have no choice other than to "rig" and do things in a way that is other than ideal. Thank you so much for the link...I will thoroughly enjoy reading that. I absolutely love you guys and how helpful you are. You are awesome!

Reply to this Comment

@Nando,

I got a chance to read over the information you posted from the link earlier...again, thank you very much...and also, I clicked through to the link he was referencing as well. I loved that material. It was awesome. I was laughing 70% of the time (at least) as I was reading, and most of the time I was reading it, I was nodding my head in agreement and remembering back to specific instances where I had struggled in the same way. I particularly love this quote by him:

  • My first problem with CSS is simply that it just never does what you tell it to. I say, "make this a column CSS" and it goes, "What? No that should go over here totally on the left and f--- you I like apples." I say, "make this fill all of the parent div" and CSS says, "Sharks love tiny needles, and no that will only take up the top part." I say, "Hey, CENTER THIS" and CSS says, "My shoes have centered worms but your heading will stay to the left."

and also

  • Seriously, in 20 years, when the world looks back on us, they'll wonder why the f---we struggled for decades with something so f---ing weird.

A good read for my Monday. :-) I think part of my problem is that with programming, I enjoy procedural program to a certain degree, and I appreciate a logical order to things. So far, working with CSS simply hasn't been logical...at all. The aforementioned way of doing things seemed a whole lot more logical and to have an order that made a whole heck of a lot more sense. Now, that being said, I am not expert on CSS, and it is quite possible that there are guys out there who can do CSS, and it is completely logical to them, I just don't get it. Yet. I may someday, though. We'll see.

Reply to this Comment

@Jon,

Cool my man - glad you liked :)

@JGarrido,

That's an excellent question. In all my thinking about why this happened, I never really stopped to think about "should" this happen. I wonder if there is something that jQuery can do about it; or, would checking the parent lead to other issues that we're not thinking about?

@Tom,

That could be a good option. Getting heights / widths of elements always makes me a bit nervous 'cause I am never sure how paddings and borders getting taken into account. But, that's just fear talking.

@Kory,

Awesome! I'm glad this helped clear it up. I had this problem for a long time as well.

Reply to this Comment

Nope.
Not fixed.
jquery changes/forces those (copy-paste from source):
cssShow = { position: "absolute", visibility: "hidden", display: "block" },

That flickering you see may be caused by margins. By definition margins collapse - so when box A has margin-bottom 20px and box B below A has margin-top 10px, then collapsed margin/distance between them is 20px, not 30px. Slidedwon animates height, padding and margins. So my advice would be to NOT use top, bottom margins on slided content and you should be fine. Use padding on outer container when space is needed instead.

Reply to this Comment

Below is the code I am using to prevent jerky sliding of the div, div3. Would you please help me in fixing this issue? The code works well in FireFox, but the div that is hidden/show jerks in IE8. Thank you very much in advance for your help!!

<!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>
<style>
.div3{
width: 300px;
height:40px;
}
.div4{
width: 300px;
height:80px;
}
</style>

<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript">

$(document).ready(function(){

$('[value="myDiv_3"]').attr('checked',true);
$('.div3').show();

$('input[name="myRadio"]').change(function(){
var selected = $(this).val();
$('.div3').slideUp();
$('#'+selected).slideDown();
});
});
</script>
</head>

<body style="text-align:center;">
<form action="example.com" method="post">
<input type="radio" name="myRadio" value="myDiv_3" />MyDiv3
<input type="radio" name="myRadio" value="myDiv_4" />MyDiv4
</form>
<div id="myDiv_3" class="div3">Div number 3!</div>
<div id="myDiv_4" class="div4">Div number 4!</div>
</body>
</html>

Reply to this Comment

Hi! I know it's an old post, but I have this problem today with a little issue extra: I didnt know the element width.
So, after undestanding the problem thanks to your explanation, Ben, I came out with this solution to my problem, that may be useful for others.

Here it gooes:

function slideFix(block, interval){ // Element to hide, interval value (int)
block.width(function(){
return block.parent().innerWidth();
}, block.slideToggle(interval).delay(interval+1).width('auto'));
}

Maybe not the best solution, but works perfect for me.

Thanks!

Reply to this Comment

Hi,

I am new to the jquery, web developing side of computer and I am trying to self teach myself using Dreamweaver. I understand this is a old post but using the script:

<script type="text/javascript" src="./jquery-1.6.3.js"></script>
<script type="text/javascript">


// Cache a reference to the hidden content.
var hiddenContent = $( "div.content" );

// Bind to the Read More link to toggle the
$( "a.readMore" ).click(
function( event ){

// Cancel the default event (this isn't a real link).
event.preventDefault();

// Check to see if the content is visible.
if (hiddenContent.is( ":visible" )){

// Hide it slowly.
hiddenContent.slideUp( 3000 );

} else {

// Show it slowly.
hiddenContent.slideDown( 3000 );

}

}
);


</script>

How do i assign multiple div's to 1 js. So for instance, I have 6 separate div's on the html page. I have content1, content2, content3, content4, content5 and content6.

Can I use the script above so that each div uses the same script but when i click "read more" only 1 div opens and not all of them or none of them?

Do i need to use the script 6 times and change the "var hidden content" or can I just simply add more div's to that selection?

Sorry if I haven't explained very well. I'm trying to learn and understand javascript but not doing very well so far.

Reply to this Comment

Old problem, but I hit it today. Here's a fiddle for the example, with the working version commented out.
http://jsfiddle.net/fpPJz/19/

It should be noted that by jQuery 1.9.1 this problem went away, it's only when touching an old 1.7.2 based project that it cropped up.

Reply to this Comment

@L.Sanders,

the best way to accomplish that is to create a relationship between the a.readMore and the div.content, somehow.
Using HTML in this very example:
if the content is NEXT to the link in the markup, you could do this
http://jsfiddle.net/fCL2L/
What If done here is to apply the slide function to the next sibling in the markup only if it is div.content.

Another way to do this is to create a relation with some attribute.
Check this out:
http://jsfiddle.net/rATxa/
In this case, I put the ID of the div.content in the href of the link (for each pair) so I take the href value and use it to find the div.content and slide it.

There must be lots of better ways and practices, but hope it could help you.

Reply to this Comment

@Ben,

Hey there,

I'm fairly new to this stuff so please bear with me. How is this problem solvable when responsive websites come into play? For instance, this is currently an issue for me because I have divs that are resizing depending on the width of the browser window. Does that mean that I'll be unable to give them a fixed width?

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.