Awhile back on Digg there were a couple posts about animated sliding boxes similar to what Digg uses for its comment system. The two more popular ones, How to Create Digg Comment Style Sliding DIVs with Javascript and CSS [1] and Re: How to Create Digg Comment Style Sliding DIVs with Javascript and CSS [2], failed to take one thing into account that annoyed me a bit. If you compare the two examples with Digg’s comment boxes you’ll notice that Digg’s fix the content of the box to the bottom where the other two fix the content to the top. Maybe it’s just me be anal, but I decided to figure out how to do it both ways.
CSS
We'll start with the cascading style sheets as they're the key to making this work correctly. The main container, the one that will actually expand and collapse, is defined below as "collapse". This needs to have a "overflow: hidden" so that during the expand and collapse the content will not display outside the bounds of the container and won't display scroll bars. Since I want my container to start out hidden, I'm also setting "display: none".
In order for this to work correctly the content container, defined below as "collapseContent" must have "position: relative".
Events
The fireEvent calls and the example at the end both depend on my previous articles Custom JavaScript Event Listeners [3] and JavaScript Built-In Listeners and Memory Leaks [4] so be sure to check those out.
Constructor
The constructor "CollapsibleBox" takes 3 arguments - duration, fixbottom, and startopen. duration is the amount of time in milliseconds for the container to open and close. fixbottom is a boolean value to determine whether the contents are fixed to the bottom of the main container (true) or not (false). startopen is a boolean value to determine whether the whether the box starts open (true) or closed (false).
I took the approach of creating the main container, "collapse", and content container, "content", programmatically. I'm sure there are plenty of arguments out there against this approach, but it's the one I prefer because it give me complete control over the behavior of the primary DOM element.
Retrieval of Containers
To place content inside the content container simply call getContent and then add your content to the returned object. getContainer has been included so you can place the main container wherever you like in the page, but also in case you need to modify the main container's styles or get information related to its layout.
Animate
The animate method is the core of this object so I'll try to step through this will a little more detail.
var height = this.collapse.scrollHeight;
This line gets the total scrollable height of the content container. This is used to determine the final height of the main container.
var fps = 30/1000;
This line sets the frames per second to animate the open/close action. I chose the de facto standard of 30 frames per second.
var fpd = this.duration * fps;
This line determines how many total frames to show over the set animation duration.
var pxPerFrame = height/fpd;
This line determines how many frames to adjust the height of the container for each frame.
The following five lines make the calculation for the new container height based on the current frame number and whether the box is opening or closing, and then set the height.
var st = newheight - height;
This line determines the difference between the new main container height and the content height. This is important for two reasons. The first is if you want the content fixed to the bottom of the container then this.content.style.top gets set to this value. The second is to check whether the animation is complete or not. If st is less than 0 and the absolute value of st is less than the content height then continue on with the animation, otherwise finish up.
The last few lines sets the animationState to 'end' to make sure the code doesn't try to open or close again then, if the box was closing, it sets display to none and fires a close event, otherwise it fires a open event.
Show/Hide and isOpen
To start an open animation the box first needs to be set to a positive, but very small, pixel value (1 in this case) and set display to block. Some object parameters also need to be set to make sure the animation performs properly; open needs to be set to true, currentframe needs to be set to 1, and animationState needs to be set to 'opening'. Finally, before starting the animation an 'opening' event gets fired.
To start a hide animation almost the exact opposite needs to happen. Since we want to see the animation the size and display values are changed at the end of the animation, but open needs to be set to false, currentframe needs to be set to 1, and animationstate needs to be set to 'closing'. Again, before starting the animation a 'closing' event gets fired.
The isOpen method is used to determine whether the box is open or closed.
An Example
The example is a little verbose, but it's the easiest way to get everything in at once. To your body tag add onload="load()" which will call the following function.
First create a new CollapsibleBox instance and then get the container and content nodes. To the content container add some, well, content. Next create two links - one which calls showBox and the other which calls hideBox. Finally append the container to body. Now when you click "Open" or "Close" you'll see the container open or close with the content fixed to the bottom of the container.
The Final Result
EDIT: 4-25-2007
I discovered a couple of bugs/omissions that should be noted.
The first is the omission of changing the display property within the constructor based on whether startopen is true or false. The following code should be included after the creation of this.collapse.
The second is a bug which presents itself if the content of the box changes sizes. The design of the box is to change size to fit it's contents (I may want to make this a parameter at some point to determine whether it's a fixed size or not). However, during the animation stage the height of the box has to be set to an explicit value. Because of this if the content changes size then the box will not resize to show all the contents.
This code:
should be changed to this:
The "Final Result" code has been modified to reflect these changes.
Example
Here are the source files:
sliding-box.html [5]
sliding-box.js [6]
Links:
[1] http://www.harrymaugans.com/2007/03/06/how-to-create-an-animated-sliding-collapsible-div-with-javascript-and-css/
[2] http://firblitz.com/2007/3/6/re-how-to-create-digg-comment-style-sliding-divs-with-javascript-and-css
[3] http://www.josh-davis.org/2007/04/10/custom-event-listeners
[4] http://www.josh-davis.org/2007/04/11/javascript-built-in-listeners-and-memory-leaks
[5] http://www.josh-davis.org/files/uploads/2007/05/sliding-box.html
[6] http://www.josh-davis.org/files/uploads/2007/05/sliding-box.js