Round Panels and Shadow Boxes CAPTCHA Image Generator
Jul 5

Printer Friendly Version

Download Source Code: AnimatedCollapsiblePanel.zip - 16.03KB

Animated Collapsible Panel Design


Title goes here...
Show/Hide
This is an image
Content goes here...

Time to add our final touch: animation. When the panel expands or collapses, the user must see its content's height gradually shrinking or growing, which actually creates the effect of animation.

Again, we'll try to keep the implementation as simple as possible, to avoid using IDs or storing intermediate values in client-side cookies.

It's obvious we need to determine what the height of the rendered content will be. This is usually hard, because we do not have a height value set, and the actual height is dynamically determined at runtime, by the browser.

However, we're lucky to have the offsetHeight property of a HTML element, but this has to be visible. When our content is collapsed and hidden, first thing to do is to make it visible (set display="block") and read its offsetHeight.

Show in new browser window
<html> <head> <link type="text/css" rel="stylesheet" href="Animated-Collapsible-Panel-5.css" /> <script language="javascript" type="text/javascript" src="Animated-Collapsible-Panel-5.js"></script> </head> <body> <!-- Animated collapsible panel, with separate CSS and JavaScript --> <div style="width:170px;"> <div class="squarebox"><div class="squareboxgradientcaption" style="height:20px; cursor: pointer;" onclick="togglePannelAnimatedStatus(this.nextSibling,50,50)"><div style="float: left">Title goes here...</div><div style="float: right; vertical-align: middle"><img src="../images/collapse.gif" width="13" height="14" border="0" alt="Show/Hide" title="Show/Hide" /></div> </div><div class="squareboxcontent"> <img width="150" height="100" src="../images/sample_photo.jpg" alt="This is an image" title="This is an image" /><br /> Content goes here... </div> </div> <img width="170" height="20" alt="" src="../images/shadow.gif" /> </div> </body> </html>

Our final implementation uses separate CSS and JS JavaScript files, referenced from the HTML page.

External .css stylesheet files can be referenced by a <link type="text/css" rel="stylesheet" href="...css" /> line in the HEAD section of the page. Here is the CSS file we used before:

.squarebox {
    width: 100%;
    border: solid 1px #336699;
    text-align: center;
    overflow: hidden; }
.squareboxgradientcaption {
    color: #ffffff;
    padding: 5px;
    background-image: url(../images/gradient_blue.png);
    background-repeat: repeat-x; }
.squareboxcontent {
    background-color: #f5f5f5;
    padding: 10px;
    overflow: hidden;
    border-top: solid 1px #336699; }

External JavaScript .js code files can be referenced by a <script type="text/javascript" src="...js"></script> line. Here is the required JavaScript JS file we separated from the HTML page:

// not animated collapse/expand
function togglePannelStatus(content)
{
    var expand = (content.style.display=="none");
    content.style.display = (expand ? "block" : "none");
    toggleChevronIcon(content);
}

// current animated collapsible panel content
var currentContent = null;

function togglePannelAnimatedStatus(content, interval, step)
{
    // wait for another animated expand/collapse action to end
    if (currentContent==null)
    {
        currentContent = content;
        var expand = (content.style.display=="none");
        if (expand)
            content.style.display = "block";
        var max_height = content.offsetHeight;

        var step_height = step + (expand ? 0 : -max_height);
        toggleChevronIcon(content);
                
        // schedule first animated collapse/expand event
        content.style.height = Math.abs(step_height) + "px";
        setTimeout("togglePannelAnimatingStatus("
            + interval + "," + step
            + "," + max_height + "," + step_height + ")", interval);
    }
}

function togglePannelAnimatingStatus(interval,
    step, max_height, step_height)
{
    var step_height_abs = Math.abs(step_height);

    // schedule next animated collapse/expand event
    if (step_height_abs>=step && step_height_abs<=(max_height-step))
    {
        step_height += step;
        currentContent.style.height = Math.abs(step_height) + "px";
        setTimeout("togglePannelAnimatingStatus("
            + interval + "," + step
            + "," + max_height + "," + step_height + ")", interval);
    }
    // animated expand/collapse done
    else
    {
        if (step_height_abs<step)
            currentContent.style.display = "none";
        currentContent.style.height = "";
        currentContent = null;
    }
}

// change chevron icon into either collapse or expand
function toggleChevronIcon(content)
{
    var chevron = content.parentNode
        .firstChild.childNodes[1].childNodes[0];
    var expand = (chevron.src.indexOf("expand.gif")>0);
    chevron.src = chevron.src
        .split(expand ? "expand.gif" : "collapse.gif")
        .join(expand ? "collapse.gif" : "expand.gif");
}
Title goes here...
Show/Hide

To show the panel initially collapsed, we do what we did before: set the display style of the content to "none" and the chevron image source to expand.gif.

We kept the not-animated collapsing panel behavior handled by togglePanelStatus. We separated changing of the chevron image in toggleChevronIcon, because this action has to be used also by the animated panel. Each animated expand or collapse will call, once, togglePanelAnimatedStatus, passing as arguments some animation tuning parameters: interval specifies the number of milliseconds between repainting the browser during the animation, and step gives the number of pixels the height is increased or decreased between two refresh sequences. Combination of these two values defines essentially animation's speed.

When a collapse/expand animation starts, the currentContent global variable is set to the panel being processed. This prevents other panels being collapsed or expanded in the same time, and also makes unnecessary to call document.getElementById from togglePanelAnimatingStatus. This is because this last function is always scheduled to run after the interval value through a setTimeout JavaScript call, and you cannot pass objects as arguments to this function!

To use these functions for both collapse and expand, we pass a positive step_height value for collapse and negative for expand. When step_height - which holds the current height of an animated panel - becomes less than 0 or larger than max_height - which holds the rendered offsetHeight value of the panel - animation ends.

Things left to do:

  • It looks like offsetHeight's value is somehow larger than the actual height. The animated panel gets extended to a bigger size, but after the final step is reset to its actual height.
  • The GIF image used as shadow does not have its "background" color set as transparent. Try to show the panel on a different background color: all appears well, but the shadow.

Continue reading »

Subscribe and Share: Subscribe using any feed reader Bookmark and Share

11 Comments

How would I code this to collapse all but the current panel?
 

2. Cristian Says:
Not sure what you mean. You have examples of collapsible panels in pages 2 and 3.
 

Sy you have 3 panels on a page with one open. If you click to open another, I would like the original open panel to close at the same time.
 

4. Cristian Says:
I see. Like a main menu: when you open another popup, any other will auto-collapse.
A quick solution can be applied for the first collapsible panel in page 2 (sorry I don't have time enough now for the code - tomorrow I go in vacation :)).
In the generic togglePannelStatus JavaScript function, insert code, at the beginning, to identify all DIVs with squareboxgradientcaption class name and collapse all panels from the page.
 

I played around with it and couldn't get it to work.
 

6. Declan Bradley Says:
Hi there,
I found your 'Animated Collapsible Panel' tutorial extremely interesting and was wondering if it can be tweaked so that the panels extend and collapse horizontally instead of vertically as in the sreenshot link below:
http://www.igolfscorer.com/widget.jpg
Any advice would be great! Thanks again for the great tutorial!
Kind regards,
Declan
 

7. Dieter Durant Says:
Any thoughts on this? I was unable to make it do this.
 

8. Dieter Durant Says:
I have 3 panels on a page with 1 open when the page loads. Is there a way to cose all open panels when I click on a closed panel to open it?
 

9. Cristian Says:
@Declan and @Dieter - check the new Advanced ASP.NET Docking Panel article and open-source project. The panels can extend and collapse horizontally (as Declan said) and implement the "accordion" effect (as required by Dieter).
 

I like this but the box is open in it's initial state. Is there any way to have it closed initially or is it suppose to be closed and I've missed something?
 

11. Cristian Says:
@Mel - in page 3 you have both cases: the box initially open AND closed (collapsed). Check also my article on Advanced ASP.NET Docking Panel
 

Leave a Reply