Accordion Widget Using the Yahoo UI

Tagged:  

As you may know Yahoo has a great user interface JavaScript library. The accordion widget is on that can be used on quite a few sites, so I have put together a little demo of this element.

Below is the code to get to the libraries that are needed for much of the JavaScript code.

<script type="text/javascript" src="http://us.js2.yimg.com/us.js.yimg.com/lib/common/utils/2/yahoo_2.0.0-b2.js"></script>
<script type="text/javascript" src="http://us.js2.yimg.com/us.js.yimg.com/lib/common/utils/2/event_2.0.0-b2.js" ></script>
<script type="text/javascript" src="http://us.js2.yimg.com/us.js.yimg.com/lib/common/utils/2/dom_2.0.2-b3.js"></script>
<script type="text/javascript" src="http://us.js2.yimg.com/us.js.yimg.com/lib/common/utils/2/animation_2.0.0-b3.js"></script>

Below is the JavaScript code that is used to access the Yahoo UI when creating the elements and events.

var AccordionMenu =(function()
{
	var YUD = YAHOO.util.Dom;
	var YUE = YAHOO.util.Event;
	var oMenuSetting = {};
	var oMenuCache = {};
	var dLastHoverTitle ;
	YUD.addClass(document.documentElement,'accordion-menu-js');
	
	function getDT(e)
	{
		var dEl = YUE.getTarget(e);
			
		if(	(e.tagName + '').toUpperCase()=='DD' )
		{	
			var dt = e.previousSibling ;
			while(dt)
			{
				if(dt.tagName &&  dt.tagName.toUpperCase() == 'DT'){break;};
				dt = dt.previousSibling
			};
			
			if(!dt || dt.tagName.toUpperCase() != 'DT'){return;}
			else{return (dt.tagName==='DT')?dt:null};
		}
		else if(e.clientX)
		{
			var found = false;
			while( dEl.parentNode)
			{
				if(YUD.hasClass(dEl,'a-m-t')){ found  = true ; break;};
				dEl = dEl.parentNode;
			};
			if(!found){return null}
			else{return (dEl.tagName==='DT')?dEl:null };	
		};		
	};
	
	
	
	function getDD(dt)
	{
		if(!dt){return null;};
		var dd = dt.nextSibling ;
	
		while(dd)
		{	
			if(dd.tagName && dd.tagName.toUpperCase() == 'DD'){break;};
			dd = dd.nextSibling;
			
		};
		if(!dd || dd.tagName.toUpperCase() != 'DD'){return;}
		else{return dd};
	};
	
	function expand(dl,dt,dd)
	{
		dl.hasAnimation +=1;
		YUD.addClass(dd,'a-m-d-before-expand');		
		var oAttr = {height:{from:0,to:dd.offsetHeight }};
		
		YUD.removeClass(dd,'a-m-d-before-expand');
		
		var onComplete = function()
		{	
			oAnim.onComplete.unsubscribe(onComplete);
			oAnim.stop();
			YUD.removeClass(dd,'a-m-d-anim');
			YUD.addClass(dd,'a-m-d-expand');
			onComplete = null;	
			dl.hasAnimation -=1;
			var dt = getDT(dd);	
			YUD.addClass(dt,'a-m-t-expand');
			if( oMenuCache[ dl.id ] &&  oMenuCache[ dl.id ].onOpen && dd.style.height!='' )
			{	
				oMenuCache[ dl.id ].onOpen(	 {dl:dl,dt:dt,dd:dd} );								
			};	
			dd.style.height = '';
		
		};
		
		var onTween = function()
		{
			if(dd.style.height)
			{	
				YUD.addClass(dd,'a-m-d-anim');				
				oAnim.onTween.unsubscribe(onTween);
				onTween = null;
				dd.oAnim = null;
			};
			
		};
		
		if(dd.oAnim)
		{
			dd.oAnim.stop();
			dd.oAnim = null;
			dl.hasAnimation -=1;	
		};
		var oEaseType = YAHOO.util.Easing.easeOut;
		var seconds = 0.5;
		if(oMenuCache[ dl.id ] )
		{
			oEaseType = oMenuCache[ dl.id ]['easeOut']?oEaseType:YAHOO.util.Easing.easeIn;
			seconds =  oMenuCache[ dl.id ]['seconds'];
			
			if( !oMenuCache[ dl.id ]['animation'] )
			{
				var oAnim = {onComplete:{unsubscribe:function(){}},stop:function(){}};
				onComplete();
				return;
			};
		};
		
		
		var oAnim = new YAHOO.util.Anim(dd,oAttr,seconds ,oEaseType);
		oAnim.onComplete.subscribe(onComplete);	
		oAnim.onTween.subscribe(onTween);
		oAnim.animate();
		dd.oAnim = oAnim ;
	
	};
	
	function collapse(dl,dt,dd)
	{
		dl.hasAnimation +=1;
		YUD.addClass(dd,'a-m-d-anim');
		var oAttr = {height:{from:dd.offsetHeight,to:0}};
		
		
		var onComplete = function()
		{
			oAnim.onComplete.unsubscribe(onComplete);
			YUD.removeClass(dd,'a-m-d-anim');
			YUD.removeClass(dd,'a-m-d-expand');
			dd.style.height = '';
			dd.oAnim = null;
			onComplete = null;	
			dl.hasAnimation -=1;	
			var dt = getDT(dd);	
			YUD.removeClass(dt,'a-m-t-expand');	
			if( oMenuCache[ dl.id ] &&  oMenuCache[ dl.id ].onOpen )
			{				
				oMenuCache[ dl.id ].onClose(	 {dl:dl,dt:dt,dd:dd} );
			};			
			
		};
		
		if(dd.oAnim)
		{
			dd.oAnim.stop();
			dd.oAnim = null;
			dl.hasAnimation -=1;	
		};
		
		var oEaseType = YAHOO.util.Easing.easeOut;
		var seconds = 0.5;
		if(oMenuCache[ dl.id ] )
		{
			oEaseType = oMenuCache[ dl.id ]['easeOut']?oEaseType:YAHOO.util.Easing.easeIn;
			seconds =  oMenuCache[ dl.id ]['seconds'];
			if( !oMenuCache[ dl.id ]['animation'] )
			{
				var oAnim = {onComplete:{unsubscribe:function(){}},stop:function(){}};
				onComplete();
				return;
			};	
		};
		
		var oAnim = new YAHOO.util.Anim(dd,oAttr,seconds ,oEaseType);	
		oAnim.onComplete.subscribe(onComplete);	
		oAnim.animate();
		dd.oAnim = oAnim ;
	};
	
	function collapseAll(dl,dt,dd)
	{
		var aOtherDD = YUD.getElementsByClassName('a-m-d-expand','dd',dl);
		for(var i=0;i 0 ){return;};
		YUD.removeClass(dt,'a-m-t-down');
		
		if(YUD.hasClass(dd,'a-m-d-expand'))
		{	
			collapse(dl,dt,dd);
		}
		else
		{			
			if( oMenuCache[ dl.id ] &&  oMenuCache[ dl.id ].dependent == false ){}
			else{collapseAll(dl,dt,dd);}
			expand(dl,dt,dd);
		};		
		YUE.preventDefault(e);
		return false;
	};
	
	
	YUE.on( document,'mouseover',onMenuMouseover);
	YUE.on( document,'mouseout',onMenuMouseout);
	YUE.on( document,'mousedown',onMenuMousedown);
	YUE.on( document,'click',onMenuClick);
	
  var oApi = {
  
	openDtById : function(sId)
	{
		var dt = document.getElementById(sId);
		if(!dt){return;};
		if(!YUD.hasClass(dt,'a-m-t')){return;};
		var dl = dt.parentNode;
		var dd = getDD(dt);
		if(dl.hasAnimation==null){dl.hasAnimation = 0;};
		
		if(dl.hasAnimation > 0 ){return;};
		if(YUD.hasClass(dd,'a-m-d-expand')){return;};
		if( oMenuCache[ dl.id ] &&  oMenuCache[ dl.id ].dependent == false ){}
		else{collapseAll(dl,dt,dd);}
		expand(dl,dt,dd);
	},
	
	closeDtById : function(sId)
	{
		var dt = document.getElementById(sId);
		if(!dt){return;};
		if(!YUD.hasClass(dt,'a-m-t')){return;};
		var dl = dt.parentNode;
		var dd = getDD(dt);
		if(dl.hasAnimation==null){dl.hasAnimation = 0;};
		if(dl.hasAnimation > 0 ){return;};
		if(!YUD.hasClass(dd,'a-m-d-expand')){return;};
		collapse(dl,dt,dd);
	},
	
	
	setting : function(id,oOptions)
	{	
		if( !oOptions ){return;};
	
		if( typeof(id)!='string' ){return;};
	
		var setMunu = function(dl)
		{	
			dl = dl || this;
			dl.hasAnimation = 0;
			oMenuCache[ dl.id ] = 
			{
				element:dl,
				dependent:true,
				onOpen:function(){},
				onClose:function(){},
				seconds:0.5,
				easeOut:true,
				openedIds:[],
				animation:true
			};
			oMenu =  oMenuCache[ dl.id ] ;
			
			if(typeof(oOptions['animation'])=='boolean')
			{
				oMenu['animation'] = !!oOptions['animation']; 
				
			};
			
			
			if(typeof(oOptions['dependent'])=='boolean')
			{
				oMenu['dependent'] = !!oOptions['dependent']; 
			};
			
			if(typeof(oOptions['easeOut'])=='boolean')
			{
				oMenu['easeOut'] = !!oOptions['easeOut']; 
			};
			
			if(typeof(oOptions['seconds'])=='number')
			{
				oMenu['seconds'] = Math.max(0 , oOptions['seconds'] ); 
			};
			
			if(typeof(oOptions['onOpen'])=='function')
			{
				oMenu['onOpen'] = oOptions['onOpen'];
			};
			
			if(typeof(oOptions['onClose'])=='function')
			{
				oMenu['onClose'] = oOptions['onClose'];
			};
		
			if(oOptions['openedIds'].shift)
			{
				oMenu['openedIds'] = oOptions['openedIds'];
			};
			
			
			for(var i=0;i

The below is the css code that is used for the look and feel.

body{
       font-family:Verdana;
       font-size:0.78em;
       background:white;
       padding:30px;
       background:black;
       color:white;}
dl.accordion-menu dd .bd{
       font-size:0.75em;
       color:#aaaaaa;
}
xmp{
       color:#FF0099;
       margin:2em 0;
       font-size:1em;
       font-family:Tahoma;
       letter-spacing:1px;
}
div xmp{
       color:white;
}
a{
       color:#CCFF00
}
address{
       float:right;
}
address a{
        color:white;
}
h3{
        color:#66CCFF;
}
h4{
        color:#FFCC66;
}
dl.accordion-menu {
	margin: 0;
	padding: 0;
	width: 15em;
	background:white;
}

dl.accordion-menu dt.a-m-t {
	margin: 0;
	background: #aaaaaa;
	padding: 0.3em 1em;
	color: #444444;
	border: solid 1px #222222;
	border-left-color: #dfdfdf;
	border-top-color: #dfdfdf;
}

dl.accordion-menu dt.a-m-t-hover{
	background:#cdcdcd;
}


dl.accordion-menu dt.a-m-t-down{
	border: solid 1px #222222;
	border-right-color: #dfdfdf;
	border-bottom-color: #dfdfdf;
}





html.accordion-menu-js dt.a-m-t{
	cursor:pointer;
	zoom:1;
}

dl.accordion-menu dd.a-m-d {
	margin: 0;
	padding: 0;
	padding:0;
}

html.accordion-menu-js dd.a-m-d{
	display:none;
}


html.accordion-menu-js dd.a-m-d-expand {
	display:block;
}

html.accordion-menu-js dd.a-m-d-before-expand {
	display:block;
	position:relative;
	z-index:-1;
	opacity:0;
	height:auto !important;
	visibility:hidden;
	overflow:visible;
}


html.accordion-menu-js dt.a-m-t-expand {
	border-left-color:#222222;
	color:black;
	background:#c0c0c0;
}

html.accordion-menu-js dd.a-m-d-anim {
	overflow:hidden;
	display:block;	
}

dl.accordion-menu dd.a-m-d .bd{
	padding:0.5em;
	border:Solid 1px #aaaaaa;
}

The below is what actually makes the elements. The dt tag holds the titles and the div inside the dd tag holds text for each accordion element.

<dl class="accordion-menu"><dt class="a-m-t">title 1 </dt>
	<dd class="a-m-d"><div class="bd">
	Lorem ipsum dolor sit amet, consectetuer adipnia. Vestibulum pellentesque porta enim. Curabitur elementum vulputate lacus. Donec quis ipsum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam dignissim sagittis purus. Nulla sollicitudin mauris sit amet purus. 
	</div>
	</dd>
		
	<dt class="a-m-t">title 2 </dt>
	<dd class="a-m-d"><div class="bd">
	Lorem ipsum dolor sit amet, consectetuer adipnia. Vestibulum pellentesque porta enim. Curabitur elementum vulputate lacus. Donec quis ipsum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam dignissim sagittis purus. Nulla sollicitudin mauris sit amet purus. 
	</div>
	</dd>
		
	<dt class="a-m-t">title 3 </dt>
	<dd class="a-m-d"><div class="bd">
	Lorem ipsum dolor sit amet, consectetuer adipnia. Vestibulum pellentesque porta enim. Curabitur elementum vulputate lacus. Donec quis ipsum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam dignissim sagittis purus. Nulla sollicitudin mauris sit amet purus. 
	</div>
	</dd>
</dl>

Click here to view the full demo. For more information and demos using the Yahoo UI click here.

Hi, I´m trying to use the "Accordion Widget Using the Yahoo UI", everything looks
great, but I want the menu to stay open when clicking on submenues, please have a look at: http://www.ergona.se.test.levonline.com/catalog/8bfdb44b-4b24-4fc5-8356-...

What I want is that the menu shouldn´t collapse when you click one of the submenues, is that possible? how should I modify the .js code? where?

Been tryin now for 3 weeks....still no result, I´m very greatful for any help or hint in right direction.

Thanks!

Hi Ernesto,

The portion of code that I believe needs to be changed is the portion with YAHOO.util.Easing.easeOut. This should be hiding the other portions of the accordion. I would recommend commenting this out to see if it does what you want.

The other thing to try is use Firebug to step through the code and see what other portions are being called when the hiding of items happens.

If none of this helps you can e-mail me your code at david[at]ajaxonomy[dot]com and I'll see what I can do for you.

Hi David,
Thank you for your reply, unfortunately it is still not working, the menu will still
not stay open when clicked on submenu item... I´m not sure how to modify the code
in YAHOO.util.Easing.easeOut.

Any tips on how to modify the code?

Thank you for your time!

Try the below modification

function collapse(dl,dt,dd)
{
/* dl.hasAnimation +=1;
YUD.addClass(dd,'a-m-d-anim');
var oAttr = {height:{from:dd.offsetHeight,to:0}};

var onComplete = function()
{
oAnim.onComplete.unsubscribe(onComplete);
YUD.removeClass(dd,'a-m-d-anim');
YUD.removeClass(dd,'a-m-d-expand');
dd.style.height = '';
dd.oAnim = null;
onComplete = null;
dl.hasAnimation -=1;
var dt = getDT(dd);
YUD.removeClass(dt,'a-m-t-expand');
if( oMenuCache[ dl.id ] && oMenuCache[ dl.id ].onOpen )
{
oMenuCache[ dl.id ].onClose( {dl:dl,dt:dt,dd:dd} );
};

};

if(dd.oAnim)
{
dd.oAnim.stop();
dd.oAnim = null;
dl.hasAnimation -=1;
};

var oEaseType = YAHOO.util.Easing.easeOut;
var seconds = 0.5;
if(oMenuCache[ dl.id ] )
{
oEaseType = oMenuCache[ dl.id ]['easeOut']?oEaseType:YAHOO.util.Easing.easeIn;
seconds = oMenuCache[ dl.id ]['seconds'];
if( !oMenuCache[ dl.id ]['animation'] )
{
var oAnim = {onComplete:{unsubscribe:function(){}},stop:function(){}};
onComplete();
return;
};
};

var oAnim = new YAHOO.util.Anim(dd,oAttr,seconds ,oEaseType);
oAnim.onComplete.subscribe(onComplete);
oAnim.animate();
dd.oAnim = oAnim ;*/
};

I also sent the completed code to you.

Hi David,

Thank you for taking your time, I really appretiate it.
Here is a sample of the function of what I´m looking for:
http://www.zapatec.com/website/ajax/zpsuite/doc/demo.html#compact.html

It just as the original menu, but when you click on the submenues, the tree
still stays open, in order to continue browsing in that specific bransch.

Been trying for weeks to modify the code....but still can´t get it working
properly, the code you sent me is one big step further to the solution of my
problem.

Regards
Ernesto

Hi, I still haven´t figured it out, could it has to do something with the cache state?
to make the browser remember the open state of the submenues?

greatful for any help!

Hi
I still haven´t found the solution to the problem with the menu,
Getting desperate...my third week trying :)

Having a deadline tomorrow on a project I´m working on, and the client
want me to use this menu..

Hi Ernesto,

I believe what needs to be done is that you need to pass a variable through the URL and then have your server side code set the correct section display based on this URL value (i.e. the query string). You'll need to change the code so that the class of the section that needs to be displayed is a-m-d-expand.

So, using my code you would see the below on the first page.

<dl class="accordion-menu"><dt class="a-m-t">title 1 </dt>
<dd class="a-m-d"><div class="bd">
Lorem ipsum dolor sit amet, consectetuer adipnia. Vestibulum pellentesque porta enim. Curabitur elementum vulputate lacus. Donec quis ipsum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam dignissim sagittis purus. Nulla sollicitudin mauris sit amet purus.
</div>
</dd>

<dt class="a-m-t">title 2 </dt>
<dd class="a-m-d"><div class="bd">
Lorem ipsum dolor sit amet, consectetuer adipnia. Vestibulum pellentesque porta enim. Curabitur elementum vulputate lacus. Donec quis ipsum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam dignissim sagittis purus. Nulla sollicitudin mauris sit amet purus.
</div>
</dd>

<dt class="a-m-t">title 3 </dt>
<dd class="a-m-d"><div class="bd">
Lorem ipsum dolor sit amet, consectetuer adipnia. Vestibulum pellentesque porta enim. Curabitur elementum vulputate lacus. Donec quis ipsum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam dignissim sagittis purus. Nulla sollicitudin mauris sit amet purus.
</div>
</dd>
</dl>

and then something like the below would be produced by the server side code.

<dl class="accordion-menu"><dt class="a-m-t">title 1 </dt>
<dd class="a-m-d-expand"><div class="bd">
Lorem ipsum dolor sit amet, consectetuer adipnia. Vestibulum pellentesque porta enim. Curabitur elementum vulputate lacus. Donec quis ipsum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam dignissim sagittis purus. Nulla sollicitudin mauris sit amet purus.
</div>
</dd>

<dt class="a-m-t">title 2 </dt>
<dd class="a-m-d"><div class="bd">
Lorem ipsum dolor sit amet, consectetuer adipnia. Vestibulum pellentesque porta enim. Curabitur elementum vulputate lacus. Donec quis ipsum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam dignissim sagittis purus. Nulla sollicitudin mauris sit amet purus.
</div>
</dd>

<dt class="a-m-t">title 3 </dt>
<dd class="a-m-d"><div class="bd">
Lorem ipsum dolor sit amet, consectetuer adipnia. Vestibulum pellentesque porta enim. Curabitur elementum vulputate lacus. Donec quis ipsum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam dignissim sagittis purus. Nulla sollicitudin mauris sit amet purus.
</div>
</dd>
</dl>

Notice that the class of the first options content changed. This will make it appear when the page first loaded.

Let me know if this makes since.

Hi David,

Thank you for the answer, how do I make this happen?
Have tried a couple of options, but not quite sure how do it..

Hi, I have been trying to get this code to function for one month.....but still can´t get it working....would be thankful if somebody could send a sample code on how to fix the problem described above, or have another solution for the menu.

thanks

I'm using the accordion menu for a website and I was wondering if I could make the layout horizontal rather than vertically stacked?

Thanks!
Tim

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <pre> <div> <blockquote> <object> <embed> <img> <param>
  • Lines and paragraphs break automatically.
  • Web page addresses and e-mail addresses turn into links automatically.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
Copy the characters (respecting upper/lower case) from the image.