I was using Mootools Fx.Slide to create a list based FAQ section, and ran into a bit of a problem while making all of the list items collapse or expand. The solution is quite simple, but not immediately obvious. In this article I’ll demonstrate how to expand and collapse all of the items in a list, and also a way to simulate an accordion. Before we go any further, check out a demo page for this article.
Applying slide effect to a single element is straight forward and looks something like this:
var collapsible = new Fx.Slide($('target-element'), {
duration: 500,
transition: Fx.Transitions.linear
});
collapsible.toggle();
What’s not immediately obvious is that, the code above takes the target element and wraps it into a div with overflow set to hidden. Whenever Fx.Slide is toggled, negative margin is applied to the target element and it, sort of, slides under the wrapping div. Take a look at the HTML before and after the effect was applied:
<!-- before the effect is applied --> <li> <div class="collapse"> <p>Some text here</p> </div> </li> <!-- after slideIn effect was applied --> <li> <div style="overflow: hidden; height: 0px;"> <div class="collapse" style="margin: -100px 0 0;"> <p>Some text here</p> </div> </div> </li> <!-- after slideOut effect was applied --> <li> <div style="overflow: hidden; height: 100px;"> <div class="collapse" style="margin: 0;"> <p>Some text here</p> </div> </div> </li>
Once you understand how Fx.Slide functions, it becomes clear that this effect cannot be applied to any particular target more than once. With that in mind, the solution for collapsing or expanding all items is quite simple – store all of the Fx.Slide references in an array, then upon a request loop through the array and for each element in it, apply the desired effect. Here is the code:
//list of target elements
var list = $$('#collapse li div.collapse');
//list elements to be clicked on
var headings = $$('#collapse li h3');
//array to store all of the collapsibles
var collapsibles = new Array();
headings.each( function(heading, i) {
//for each element create a slide effect
var collapsible = new Fx.Slide(list[i], {
duration: 500,
transition: Fx.Transitions.linear
});
//and store it in the array
collapsibles[i] = collapsible;
//add event listener
heading.onclick = function(){
collapsible.toggle();
return false;
}
//collapse all of the list items
collapsible.hide();
});
//collapse all
$('collapse-all').onclick = function(){
headings.each( function(heading, i) {
collapsibles[i].hide();
});
}
//expand all
$('expand-all').onclick = function(){
headings.each( function(heading, i) {
collapsibles[i].show();
});
}
Also, by adding just a few more lines of code we can simulate an accordion:
headings.each( function(heading, i) {
//for each element create a slide effect
var collapsible = new Fx.Slide(list[i], {
duration: 500,
transition: Fx.Transitions.linear
});
//and store it in the array
collapsibles[i] = collapsible;
//add event listener
heading.onclick = function(){
//open current element
collapsible.toggle();
//hide the rest
for(var j = 0; j < collapsibles.length; j++){
if(j!=i) collapsibles[j].slideOut();
}
return false;
}
//collapse all of the list items
collapsible.hide();
});
Take a look at the source code of the demo page to see more elaborate code examples.
Scope says:
hey this is nice work…how would you go about modifying it so that the accordions are open/closed by a mouse hover?
alex says:
It’s quite simple, really - just use onMouseOver instead of onClick:
heading.onmouseover = function(){Or you can use onMouseEnter:
heading.addEvent('mouseenter', function(e){ var span = $E('span', heading); if(span){ var newHTML = span.innerHTML == '+' ? '-' : '+'; span.setHTML(newHTML); } for(var j = 0; j < collapsibles.length; j++){ if(j != i) { collapsibles[j].slideOut(); if(spans[j]) spans[j].setHTML('+'); } } collapsible.toggle(); return false; });Papi says:
Really cool stuff! For the accordion, they are all closed initially. How would you set it to expand the first “item” when the page loads?
Alex says:
You could just place this code “collapsibles[0].toggle();” at the very end of the accordion function - that would collapse all items and then open up the first one.
More efficient way, however, would be to replace “collapsible.hide();” with “if(i != 0) collapsible.hide();” - that would leave the first element open.
Un-deprived.com » Blog Archive » Mootools - Scroll.js note says:
[…] thing to note is a customized Accordion effect . I was looking for a way to use mootools to review an answer when the user click on a […]
Sml says:
Hi,
thank’s for your quick and very clear tutorial (and the more elaborate demo). Exactly what I needed for a better understanding on how deal in mootools with multiple items and to apply effects on them.
Alex says:
Hi Sml,
while I can’t really tell “exactly” what you need for better understanding of Mootools, I can recommend using Firebug plugin for Firefox - it is an absolute must-have for a web developer.
Green says:
hoi, this is a very nice work. :) how I can modify the vertically slide effect, so that only are defined boxes open. so, the default values overwrite via
window.addEvent('domready',...)the js-file. the js-file should remain unchanged. thank.Alex says:
Green, I believe this particular question has already been discussed on Mootools forums - just take a look around there.
Aram says:
Great demo. Is it possible to have the “accordion” inside the “slide-effect”?
Alex says:
Aram, that should be fairly easy to accomplish. Just add another level to the list and attach accordion behavior to it - simple :)
Pravin says:
Great Work …..
when i tried with onMouseOver its slides in nicely but i need to mouseover again to slide out…. Is there any solution for this…..
It should be like when ever the mouse is over the heading it slides the content. and when ever the mouse is out of heading or content It should slide out….
Alex says:
Pravin, you should use onMouseOut instead.
jan says:
hi, nice work! how did you manage to add the round corners to the gif’s and how can i manage to get rid of this function? i couldn’t find the relating parts in the script. thank’s a lot, cheers, jan
Alex says:
Hi Jan, the rounded corners have nothing to do with the script itself. They are applied via CSS. Use your own style sheets to make the slides look however you wish :)
jan says:
hi Alex,
i know, i was just curious how they were created, couldn´t figure it out so far…what’s the css method called to create them, so i can google it. i still got no clue looking at your stylesheet :-). cheers from freiburg/germany, jan
Alex says:
Jan, CSS (Cascading Style Sheets) is beyond the scope of this article. Basically, it is the glue between HTML and graphics. I strongly recommend that you learn HTML/CSS basics, before jumping into JavaScript.
Daniel S says:
Hey, awesome script…
but how would i go about creating a collapse container in a container….
so in the demo .. i wish to have another accordion inside an accordion (item 1, then another button to collapse inside it) … thanks.
Alex says:
Daniel, first of all, I wouldn’t recommend using such structure - it will make finding a particular slide rather hard. If you still wish to use nested accordions, it should be fairly simple to make. If you need help on this matter, please go to MooTools forums.
Jeremy says:
Awesome script, this saved my butt when I couldn’t get other accordion scripts to work in IE6.
Is there a way to toggle a specific collapsable after the script has been loaded? I’m trying to create a link to toggle a certain collapsed layer, but can’t figure out how to do it.
Thanks again!
Alex says:
Jeremy, sorry for the late reply. Yes, it’s possible - the solution is quite simple, in fact. Just add attribute rel=”dontcollapse” to the divs you wish to keep open and take a look at the code below:
headings.each( function(heading, i) { //for each element create a slide effect var collapsible = new Fx.Slide(list[i], { duration: 500, transition: Fx.Transitions.linear }); //and store it in the array collapsibles[i] = collapsible; //add event listener heading.onclick = function(){ collapsible.toggle(); return false; } //collapse all of the list items except for elements with rel="dontcollapse" var dontCollapse = collapsible.getProperty('rel'); if(dontCollapse != 'dontcollapse') collapsible.hide(); });I haven’t had any time to test the code, but in any case you should be able to get it running :) Also, you may want to check out MooTools forums, there is a solution (for this particular script) that uses cookies to remember slides’ positions.
tomms says:
hey great script, quick question…
Is there anyway to make a “slide all” button, where instead of hiding/showing all of them, a single button slides/unslides them all
thank you
Alex says:
Use toggle method instead… You can find out more in MooTools official docs.
tomms says:
thanks alex, im working on it right now
what im trying to do is have multiple sliding menus on a single page, in different areas and sliding in different directions all countrolled by a single button
do you think this would be possible with toggle?
Alex says:
I’m not really sure of what you are trying to accomplish. There are only 5 methods: hide, show, toggle, slideIn, slideOut - I think their intention is obvious. With the right combination of those you can achieve the effect you’re after. Once again, if you need some more info on those methods - check the official docs.
Jon Crain says:
Is there a way to have it not flash the content of the collapse div when loading?
Alex says:
You may just have a lot of content or a slow PC - I haven’t really came across this issue. What you are after, however, is quite simple to accomplish, but it will create accessibility issues.
At the moment all of the slides are open by default and collapsed once DOM is loaded, with the use of JS; if a user’s web browser does not support JS, then there will not be a slide/collapse effect, but all of the content will still be readable/visible.
You can hide all of the content with the use of CSS. Doing so will rid of the content flash completely, but if a user’s browser does not support JS, they won’t be able to see anything inside the slides.
Hope that made sense :)
Jon Crain says:
I don’t have that slow of a PC. It’s a newer Intel Mac with 2GB of ram. I am loading content from a database, would that make a difference? I’m also having a heck of a time getting the slide boxes to show all of the content. Any CSS tips on that? It doesn’t seem to have a rhyme or reason. The test server is at http://141.209.36.46/somsite/events/concerts/
I’ve tried setting the height to auto and 100%, but the only thing that seems to work is setting a defined height, which I don’t really want to do.
Alex says:
I see that you’re using slides as an accordion - that is not the best way to go. I’ve only included it in the demo as a proof of concept, I guess. The accordion plug-in, packed with MooTools, will work much better in your case.
As for the hidden content - just add a bit of extra padding at the bottom should do the trick.
tomms says:
hey alex, thanks for your quick responses, i found what i was after…
can you post a link or a quick code for a cookie that remembers the position of the menu? (i tried looking on the forum but i wasnt able to implement the code to this script)
Jon Crain says:
What is it that will work much better? I can’t seem to get the built-in accordion to do what I want. For some reason I can’t figure out how to have it load with all collapsed, and have the + turn to -. Mainly because I have no javascript skills. I am learning though. Thanks for the help you’ve been.
Alex says:
@tomms - I don’t have the links, sorry. It should be there, I’ve seen quite a few referrers from it as one point in time.
@Jon - The Accordion plug-in just seems much faster and responsive. It doesn’t allow for all of the slides to be closed, I believe.
Jon Crain says:
Thanks Alex, you’ve been more than enough help for me. I’ve got it working using the standard accordion now. Now all I have left is to figure out that little + - change you got going on there…
Mara Alexander says:
Thank you Alex!!! You explained the fx.slide in a way I wasn’t getting from either the docs or the mootools forum. It makes SENSE now:)
Dan says:
What can be added to the javascript to force the first element in the accordion list to be open when you first go to the pagee?
tyler says:
I noticed the collapse element does indeed ‘flash’ or flicker its visibility while collapsing (sliding). Tested on multiple browsers/computers. Ive noticed a similar type of behavior in mootools when visibility is set to 0, but I’m not sure that’s what causing this. I need to dig around the Slide class to see whats going on w visibility. But this flickering is an issue here.
You said “using slides as an accordion - that is not the best way to go. I’ve only included it in the demo as a proof of concept, I guess. The accordion plug-in, packed with MooTools, will work much better in your case.”
Well, accordion would be perfect if it collapsed all elements which it doesn’t. Bummer if your Slide accordion isn’t the solution b/c of the flicker. …
Thanks for the work.
tyler says:
You can see it flicker better with taller elements. here:
http://ziiglab.org/test/accordoslide.php
Alex says:
I only have a bit of flickering in Firefox 2.* under Windows XP. Other browsers run rather smoothly, including Firefox 2.* under Linux. Not really sure where the problem lies - whether it’s Firefox’s engine or perhaps inefficient use of code. I’ll look into this though, in the near future. Perhaps MooTools 1.2 will resolve this, if not I may come up with a custom slide plugin - we’ll see :)
tyler says:
OK Alex, I see you are just demoing some slide stuff here which is cool.
Here is the solution for collapse all accordion if anyone is interested:
First use the extended accordion here-
http://forum.mootools.net/viewtopic.php?id=2713
And for options on initialize, the documentation in mootools explains two options: alwaysHide and display. Initialize in the following way (note to change to your togglers and panels):
myAccordion = new ExtendedAccordion('togglers', 'panels', {display:-1, alwaysHide:true});
And that will do it with accordion.
Thanks for the slide example, would be interesting to see whats making it flicker. Ive seen mootools have other strange flickering bugs in other fx.
Alex says:
Tyler, it’s not just a demo - the purpose of this article is to show how to manipulate multiple slides at once. The solution for this is not immediately obvious, as MooTools modifies the original DOM structure. Accordion is just a byproduct here.
Thanks for your input though, perhaps it will help someone :)
Andrew says:
Hi Alex,
Great code. Here is rather simple solution to include the use of cookies to remember the state of the panel. It sets a cookie when the panel is clicked and removes again it when closed. It will show / hide the panel (without transition) when the page is loaded based on whether or not the cookie exists.
It probably can be refactored better but I hope it is of some use to someone!
collapsibles[i] = collapsible;
if (Cookie.get("menu_"+i)) {
collapsible.show();
}
else {
collapsible.hide();
}
heading.onclick = function(){
var span = $E('span', heading);
if(span){
var newHTML = span.innerHTML == '+' ? '-' : '+';
span.setHTML(newHTML);
}
if (Cookie.get("menu_"+i)) {
Cookie.remove("menu_"+i);
}
else {
Cookie.set("menu_"+i, true);
}
collapsible.toggle();
return false;
}
//collapsible.hide();
Webdesign Ansbach says:
Thank u very much for the cookie-code!
danilo says:
Hy! Your works is great!!
I’ve the question: i want open one or more div at the start page. I’ve past the code you write in recent comment but don’t work.
You can help me, please?!!
Sorry for my english but don’t speak very well!! :(
danilo says:
Sorry… I forgot one question: how can I make the previous div automatically close when I click on the next one?
Thanks!
Andrew Williams says:
Hi, i have run into a few problems with this script. My first problem is that the collapser tags are cutting my table off by about 300 pxs. Secondly the collapse tags don’t like large styled divs in them. Is there a width/length limit for these collapse tags?
Alex says:
Andrew, there are no limits, the script adjusts to dimensions automatically. My guess is that something is wrong with your CSS.
Andrew W says:
Ah i found the problem. One of my inner content areas had content floating left which for some reason makes it disappear (Fixed by putting content into nested tables.) and the other was due to a length in my css file. Thanks Alex.
mike says:
Hi
Great code!
Is it possible to have the url of the list based FAQ section to see your work please.
Thks
Jonah Dempcy says:
I see you use the traditional onclick assignment for registering click event handlers. Any particular reason why you don’t use MooTools addEvent()?
Incidentally, I wrote an article comparing MooTools’ mouseenter and mouseleave events to core JavaScript onmouseover and onmouseout, respectively:
http://www.thetruetribe.com/2008/05/mootools-mouseenter-and-mouseleave.html
hOtTiGeR says:
Hi,
I see how you have the + / - change when you click the heading.
Is there a way to use a background image on a span or even an image tag via and have an image change when you click the heading?
Thanks
Jess says:
Wow what a great example! Thank you for putting it together for us. And that’s very useful cookie code Andrew, thanks!
I have a question: I know you can toggle a single div to show all or none of the content. But is it possible to have part of the div showing at all times, say the top 40%, and the rest hidden until toggled? I know the bottom part can be a separate div but for me it has to be only one div. Is that possible do you think?
Harry Walter says:
Hi,
Great work, exactly what i was looking for! I’ve been building an entire site based around Mootools to provide an interactive user experience and your code helped me finish off the bits i couldn’t work out.
Regards Harry
xphr says:
This comment thread scares me. I feel like a lawyer or doctor where there are twice as many no-nothings fronting as developers as there are actual developers. Please stop abusing MooTools, it is a great framework, but all of you knuckleheads are going to give it a bad name. Accordions inside of accordions? Come on, stop being ridiculous and learn how to code a basic static page before you go copy/pasting effects all over your sites. Alex, you’re a man among boys.
Liam says:
Hi,
Nice code but I have been looking to replace + and - and you can do this fine with text but in terms of the accordion when you want to say use - View ^ with ^ being a down image for view and Close ^ being up to close if you set two variables for view and close and in them have say ‘close ‘ while replacing close will show the image and function correctly if you try and add an image in terms of the view the code works but the close view switching breaks.
Any ideas? I was thinking going old school a little and instead of doing innerHTML you could do document.write and replace instead which would solve the problem but that means a full re-write in terms of the span.
One good feature to add to this though is display:block; to the h3 or if you change it to a div etc that way the whole bar basically would be a link and it feels a bit more user friendly.
Alex says:
That’s it, seems that there is nothing further to contribute here - I’m closing the comments.