Saturday, March 1, 2008

Mootools Fx.Slide and Multiple Divs

While working on my own website I decided to switch from prototype.js to mootools.js so that I could use a lightbox style popup for flash videos. Unfortunately for me I had already spent a few hours setting up a list of collapsible items using prototype.js's Effect.toggle. "No big deal" I thought "I'll just replace the onclick="Effect.toggle('div_id')" with the mootools equivalent. Needless to say it was not going to be that easy.

One major bonus of using mootools is it's 'domready' capability, which can tack on js functions to normal html tags without the need to add extra parameters. One major downside (for me atleast) is it's pretty damn difficult to write a function that will take care of a specific effect for a bunch of differently named divs. You essentially have to write one function for each target div, for example:

Let's say I want to Slide.toggle a div named "this_div." Here's the code:

var mySlide = new Fx.Slide('this_div');

$('toggle').addEvent('click', function(e){
e = new Event(e);
mySlide.toggle();
e.stop();
});

Notice that "this_div" is hardcoded into this script. You may ask "Why not just wrap the whole thing in a function, pass it parameter, and activate using onclick="toggleSlide();" in the html?" Valid question. I tried doing exactly this for about 2 hours before I finally gave up... For some reason, if you wrap the above done in a function on the first toggle event takes place; any subsequent events do not take place on that element. I'm not a mootooler or a js guy so I really don't know why this was happening.

After much googling I found this post and I was able to adapt it to suite me needs. Basically, the poster recommends implementing the following function to recursively do through the child elements of an element, hide the description divs, and then toggle the area you want to click:

window.addEvent( 'domready', function(){
$$( '.moreInfoWrapper' ).each(function(item){
var thisSlider = new Fx.Slide( item.getElement( '.moreInfo' ), { duration: 500 } );
thisSlider.hide();
item.getElement( '.divToggle' ).addEvent( 'click', function(){ thisSlider.toggle(); } );
} );
} );

So the html to accompany this would be:

<div class='divToggle'>
<h3>Click to Show/Hide</h3>

</div>
<div class='moreInfo'>
<h3>More Info About Item 1</h3>
<p>Here is some content.</p>
<p>More Content</p>

<p>End of Content</p>
</div>
</div>
<div class='moreInfoWrapper'>
<div class='divToggle'>

<h3>Click to Show/Hide</h3>
</div>
<div class='moreInfo'>
<h3>More Info About Item 2</h3>
<p>Here is some content.</p>

<p>More Content</p>
<p>End of Content</p>
</div>
</div>

If you are trying to manage multiple FX.Slide effects on multiple dynamically generated divs using mootools, this approach is the best way I've found.

14 comments:

Phil said...

Thanks, this solution worked great for me.

ibesz said...

This is a nice solution. To bad though i'm searching for a way to make around 100 DIVs able to slide. I need to create all these entries from a database and wonder if anybody knows or came across a solution that works directly with Classes or in any other way with a lot of objects.

Thanx for any Information.

Markus said...

Thank you, exactly what I was struggling with. Your post helped me a lot!

Wolverine said...

check this :
http://www.sitepoint.com/article/unobtrusive-javascript/

here you can hide or show the sidebar... i am searching this from last week... but all in vain..can you help me to do that for blogger...??

Inverse Chi said...

This is very useful - thank you :)

designermonkey said...

Is there any way to do this nested? I need to do it to about 7 levels deep...

urvi said...

I was finding this for my wp theme...
Thanks a ton
Thats such a relief

alwinsblog said...

Thanks a lot...this solution worked for me also. I have used it in one Joomla extension...

Атанас Марков said...

ibesz,
just try this passage of code:

$$('.toggleme').each(function (i,x,a){
i.hide();
$(i.id+'_lnk').addEvent('click',function(){
this.toggle();
return false;
}.bind(i));
});

and make your divs this way:

<div id="div1" class="toggleme">
here you put your hidden content...
</div>

<a href="javascript:void(0)" id="div1_lnk">Toggle div 1</a>

Basically this code uses the mootools $$ function for getting elements by class name in array and the .each array method that calls a function with parameters (item,index,array).
I'm searching for sth that can help me to have a hidden div initially and display it with the toggle method without all the dom ready stuff.

Атанас Марков said...

I solved my problem using an "ugly" way to do my job. Maybe this will help someone else.
As sometimes we need a hidden content that shows on click only and don't want it displayed at page load, we may use sth like this:
1. we create two divs:
<div id="hiddendiv" style="display:none">
<div id="togglediv"></div>
</div>
inside the togglediv we put our html code. In js somewhere after defining these(I an a kind of YSlow and Pagespeed maniak, so ondomready is not the best solution for me as I don't want to wait for ads and so on.) we call a little code:
$('togglediv').hide();
$('hiddendiv').setStyle('display','block');
In my case I make a little link placed on screen with fixed position that has to open a dashboard panel where you control bookmarks, messages and so on and has to be shown on click only as it is a kind of working desktop for administrative purposes and is not a part of the main content, so the user will get frustrated if it's visible without a click while waiting for js to load and the dom ready event. A little drag and drop stuff for a site... This may be used for sliding side menus and so on too.
I don't know why mootools doesn't allow style="display:none" for initialization of toggled items, but after some changes inside the main mootools code I decided not to make such if there is a way to use tricks so there's no class to extend fx.slide and take care of the display property.

ניר said...

thank you, thank you, thank you.
very clear post, helped me a lot.

Harley said...

This is awesome stuff. Thank you!

How would I go about using this with a Wordpress theme? Just as your example, it runs perfectly on a static HTML page, however not at all with Wordpress.

Sorry to revive an old thread, but any help would be greatly appreciated..!

Thanks,
Harley.

hollen949b said...

Thanks! Glad I found this blog.

pordux said...

thabks, this really help!!!