Flash Without Flash: Accordion
August 15th, 2007 in flash without flash | 7 Comments
I guess I’ll start a series “Flash Without Flash” that tries to mimic flash effects using Mootools. It’s easier to copy something than it is to come up with something new, plus it’ll show that there are alternatives to Flash. This time around, I’ll be creating an accordion effect similar to the one found at Openfield Creative. Here’s the final product.
This is the structure I finally settled upon:
<div id="accordion">
<div class="section">
<div class="wrapper">
<div class="content">
<h2>Header</h2>
<p>Description</p>
</div>
<img src="img1.jpg" alt="image" />
</div>
</div>
...
</div>Repeat as many sections as you like. The structure inside the section div is simply to recreate the Openfield layout. It’s not necessary; change it if you like.
Much of the CSS is used to recreate the structure, too. But there are a few key classes.
body { background-color:#66B9CC; font-size:75%; font-family:Arial, sans-serif; } #accordion .section { overflow:hidden; height:200px; margin:2px; background-color:#D9EEF2; } #accordion .section .wrapper { width:1500px; } #accordion .section .content { float:left; width:250px; padding:0 10px 10px 100px; color:#960; } #accordion .section .content h2 { margin:15px 0 20px; } #accordion .small { cursor:pointer; height:50px; } #accordion .small:hover { background-color:#fff; } #accordion .hidden { margin:0; height:0; }
We’ll be using #accordion .small and #accordion .hidden to change the display of our sections as they’re moved to the edges and finally disappear. Since we won’t be using the display property to hide the sections, the margin must be set to zero. If you plan on adding any borders, those will have to be reset also. Each section’s overflow has to be hidden because the height will be adjusted, and we don’t want the content showing through.
Since this is more complex, I’ll be storing all the functions and variables in an object, which should generally be done to prevent the namespace from becoming polluted.
var OFAccordion = { }
I’ve called it OFAccordion because it’s an accordion in the style of Openfield’s. You can call it whatever makes sense to you.
We’ll start off with the initialize function that we’ll call to get it all going.
start: function () { this.sections = $$('#accordion .section'); this.fx = new Fx.Elements(this.sections, { wait:false, duration:500, transition:Fx.Transitions.Circ.easeOut }); this.sections.setOpacity(0.25).addClass('hidden'); this.active = false; this.current = 0; this.o = { phase1:{}, phase2:{} }; this.display(0); },
First we grab all of the sections and make a new Fx.Elements with them. This allows us to start effects on any or all of the elements at the same time. Check out the Mootools docs for more info on that. I hadn’t used it until now, and it’s pretty handy for stuff like this. Next we set the opacity of all the sections to 25% and add the class hidden. Then we initialize a few variables: active, which we’ll use later to determine whether the effect is active or not; current to store the current section that’s visible; and o.phase1 and o.phase2 to store our effect values which will be chained later, thus the two phase objects. Everything will become clearer as we go along. Finally, we call a function (that hasn’t been defined yet) that will show whatever section index we pass in. In this case, it’s the first section.
Next is the display function:
display: function (index){ this.active = true; this.o.phase1 = {}; this.o.phase2 = {}; this.hideOuter(index - 2); this.hideOuter(index + 2); this.hideInner(index - 1); this.hideInner(index + 1); this.sections[index].removeClass('small') .removeClass('hidden') .removeEvents('click') .setOpacity(1); this.o.phase1[index] = {height:200}; this.fx.start(this.o.phase1).chain(function () { this.fx.start(this.o.phase2); }.bind(this)).chain(function () { this.active = false; }.bind(this)); },
First, we set active to true so later on, we can check it the effect is already in progress to make sure we don’t start it again. Then we clear out the effect values. Now we call a few functions that aren’t defined yet. Essentially, they prepare the sections for the transition and populate the the effect values. After that, the section to be displayed is prepared by removing the small and hidden classes, setting the opacity to 100%, and removing all onClick events. The effect value is set to transition the section’s height to 200. You can change this to the scroll height of the section if you like, but for this example, I wanted the sections to be a uniform height. Finally we start the effect by passing in the effect options for phase one, then chain the effect for phase two, and finally chain a function to set active to false. I know the syntax for chaining looks a little complex, but it’s pretty useful. Take a look at the chaining docs to get a better idea of how it works. We have to use some binding so when the function is executed this refers correctly to the effect.
Now we have a couple helper functions:
displayNext: function () { if (this.active == true) return; this.display(++this.current); }, displayPrevious: function () { if (this.active == true) return; this.display(--this.current); },
These functions will be attached as click events to the upper and lower sections. All they do is see if the effect is active. If it’s not, call display() and pass a decremented or incremented current index value.
These last couple functions prepare the sections to be hidden or shown smaller:
hideOuter: function(index) { if (this.sections[index]) { this.sections[index].removeClass('small') .addClass('hidden') .setStyle('visibility', 'hidden') .removeEvents('click'); this.o.phase1[index] = {height:0}; } }, hideInner: function(index) { if (this.sections[index]) { this.o.phase1[index] = {height:50}; this.o.phase2[index] = {opacity:0.25}; this.sections[index].removeClass('hidden') .addClass('small') .setStyle('visibility', 'visible'); if (index < this.current) { this.sections[index].addEvent('click', this.displayPrevious.bind(this)); } else { this.sections[index].addEvent('click', this.displayNext.bind(this)); } } }
In hideOuter(), we make sure it has the class of hidden and set its visibility to hidden. The only reason we have to do this here and not in the CSS is because Mootools changes the visibility property when adjusting opacity. Any click events that may have been assigned are removed, and the transition to a height of zero is set. The hideInner() function makes the section appear with a height of 50 and after that, an opacity of 25%. We also need to make sure it’s visible. Finally, we add a click event that will display the next or previous section.
Rememer, to get everything started we need to do this:
window.addEvent('domready', function(){ OFAccordion.start(); });
I use the domready event that Mootools provides instead of a load event because we have a lot of images, and the script won’t do anything until they’re all loaded if we use a load event. With the domready event, the DOM is completely loaded so we can start manipulating it before the images themselves have loaded.
Here is the demo again.
Now for my critique of this method. This type of accordion only displays three sections at a time; the user can’t navigate to any sections that aren’t adjacent to the current one. Such linear viewing is probably best with wizards and forms, not displaying content that users are likely to want to jump around to. Just keep that in mind if you choose to use this.

