Then I came to the Moo.fx accordion and that was the good one.
Still, this Accordion has some limitations:
- If your page is large, your browser won't scroll properly to the selected accordion tab.
- It would be better if the user could click on an expanded tab to collapse it back/
- You might want to have a collapse/expand button on each tab.
- You might want your accordion to degrade gracefully for non JavaScript enabled browsers (accessibility + SEO optimization) and have links to separate pages instead of tabs in this case.
- You want to disable the Mootools Accordion from following those accessibility links because you just want to expand the tab instead. The default Accordion implementation really sucks to this respect.
- You might want a tab CSS rollover effect.
Issue #1:
Use the prototype scrolling feature to scroll to the expanded tab (I'm using Moo for Prototype):
myAccordion = new fx.Accordion(myLinks, myDivs, {onActive: function(toggler, element){
setTimeout("Element.scrollTo('" + toggler.id + "');window.scrollBy(0,-150)", 400);
});
Issues #2 and #3:
hack Accordion a bit to allow collapsing back any tab. And also add/remove a class name to the toggle DOM element in order to then use different CSS styles, for instance to display a different button backgound-image when to tab is collapsed or expanded.
Here is my modified Accordion class, I simply changed the showThisHideOpen function :
showThisHideOpen: function(iToShow){
var forceHide = iToShow == this.previousClick;
if (!this.options.alwaysHide){
if (forceHide)
this.previousClick = null;
else
this.previousClick = iToShow;
var objObjs = {};
var err = false;
var madeInactive = false;
this.elements.each(function(el, i){
this.now[i] = this.now[i] || {};
if (i != iToShow || forceHide){
this.hideThis(i);
$(this.togglers[i]).className = $(this.togglers[i]).className.replace(' expanded', '');
} else if (this.options.alwaysHide){
if (el.offsetHeight == el.scrollHeight){
this.hideThis(i);
madeInactive = true;
} else if (el.offsetHeight == 0){
this.showThis(i);
} else {
err = true;
}
} else if (this.options.wait && this.timer){
this.previousClick = 'nan';
err = true;
} else if (!forceHide){
this.showThis(i);
$(this.togglers[i]).className += ' expanded';
}
objObjs[i+1] = Object.extend(this.h, Object.extend(this.o, this.w));
}.bind(this));
if (err) return;
if (!madeInactive) this.options.onActive.call(this, this.togglers[iToShow], iToShow);
this.togglers.each(function(tog, i){
if (i != iToShow || madeInactive) this.options.onBackground.call(this, tog, i);
}.bind(this));
return this.custom(objObjs);
}
}
Issue #4, #5 and #6
We can consider it all together. For good accessibility and degradability, we need our accordion tooggle elements to be standards 'a' links. In that a href element, we put a link to a page that will display approximatively the same content than in the tab, possibly with a restful URL so that's good for SEO optimization.
Also, now that we have those a with their href, we are able to make a CSS rollover using the a:hover CSS pseudo selector to translate the background image of our toggle element. Remember: IE6 only accept the :hover selector on links and moreover the href of those links have to be defined for the :hover selector to apply properly.
But the catch is that if you use links with href as Mootools Accordion tooggle elements, your browser will follow the links and refresh the window instead of collapsing/expanding your accordion tabs. Ending your onActive custom function doesn't fix this nasty behavior. The only way I found was to patch the Accordion widget and add the
return false;
statement in the initialize function in the onclick trigger of Accordion.js:
initialize: function(togglers, elements, options){
this.now = {};
this.elements = $A(elements);
this.togglers = $A(togglers);
this.setOptions(options);
this.extendOptions(options);
this.previousClick = 'nan';
this.togglers.each(function(tog, i){
if (tog.onclick) tog.prevClick = tog.onclick;
else tog.prevClick = function(){};
$(tog).onclick = function(){
tog.prevClick();
this.showThisHideOpen(i);
return false;
}.bind(this);
}.bind(this));
this.h = {}; this.w = {}; this.o = {};
this.elements.each(function(el, i){
this.now[i+1] = {};
//el.style.height = '0';
el.style.overflow = 'hidden';
}.bind(this));
switch(this.options.start){
case 'first-open': this.elements[0].style.height = this.elements[0].scrollHeight+'px'; break;
case 'open-first': this.showThisHideOpen(0); break;
}
},
for details: have a look to Livetribune. I'm still working on this site, so please be indulgent if you are coming in a bad time. Livetribune will be ready in October.
Hope you can now build your ultimate accordion widget!
Raphaël Valyi.