Menu Accordion com JQuery

11 07 2008

Bem pessoas, o fato qual me levou a desenvolver este menu foi a necessidade de manter um controle no mesmo, podendo fechar e abrir o mesmo item do submenu, tudo isso usando ul li, direto ao ponto.

Primeira coisa a fazer, como verificar qual menu estava aberto antes do reload?
A solução para isso foi gravar o menu em cookie para verificação, segue abaixo as funções para criar e recuperar o cookie.

Função para recuperar o cookie:

/**
 * Retorna o valor do cookie
 * @autor Deusimar Ferreira 
 * @param string name
 */
getCookie = function(Name){ 
	var re=new RegExp(Name+"=[^;]+", "i");
	if (document.cookie.match(re))
		return document.cookie.match(re)[0].split("=")[1];
	return null;
}

Função para criar e setar o valor do cookie:

/**
 * Define cookie
 * @autor Deusimar Ferreira 
 * @param string name
 * @param string|int value
 */
setCookie = function(name, value){
	document.cookie = name + "=" + value;
}

Cirei um atributo para meu submenu chamado de menuIndex.

// Pega submenus 
var subContents = $('#menu li ul');
				
// Percorre o menu e adiciona indice
$("#menu li div a").each(function(index) {
	subContents.eq(index).attr({menuIndex: index + 'c'});
})

Este script contém os eventos accordion do menu, ele faz a verificação de qual submenu esta aberto e chama o evento, caso o item clicado esteja aberto ele será fechado ou inverso, caso seja outro item ele abre o item clicado e fecha os outros itens.

$("div a").click(function() {
	//$("#menu li ul:visible").slideUp("slow"); // Esocnde menu item que visivel
	//$(this).parent().next().slideToggle("slow"); // Mostra o menu item que recebeu o evento click		
	
	// Criar objeto gobal do parent span a 
	var _this = $(this).parent().next();
	
	// Percorre o menu para verificar as ações que deve ser executada
	// Verifica qual submenu deve ser fechado e qual será aberto
	$("div a").each(function(index){
		if ($('#menu li ul:eq(' + index + ')').attr('menuIndex') != _this.attr('menuIndex')) {
			// Verifica se exite algum submenu que não seja o que recebeu o evento do click aberto
			if ($('#menu li ul:eq(' + index + ')').is(':visible')) 
				$('#menu li ul:eq(' + index + ')').slideUp("slow"); // Esocnde submenu item que visivel
		} else {				
			(_this.is(':visible')) ? _this.slideUp("slow") // Esocnde submenu item que visivel
					       : _this.slideDown("slow"); // Mostra o submenu item que recebeu o evento click
		}
	})
	
	// Retorna falso funciona como um pause
	return false;
})

Neste outro script ao fechar a página ele recuperar o índice do menu e cria um cookie com seu valor.

// Evento window unload
$(window).bind('unload', function(){
	$('#menu li ul').unbind(); // Remove eventos
	var expandeIndices = []; // Array indice
	
	$("#menu li ul:visible").each(function(index){ 
		// Insere o valor do menuindex no array
		expandeIndices.push($(this).attr('menuIndex'));
	})
	
	expandeIndices = (expandeIndices.length == 0) ? '-1c' : expandeIndices;
	setCookie('menuIndex', expandeIndices); // Seta o cookie e seu respectivo valor
})

Pega o valor do cookie e mantém o respectivo submenu aberto.

// Verifica o cookie para ver qual submenu deve ser mostrado
$("#menu li ul").each(function(){
        // Mostra o submenu
	if ($(this).attr('menuIndex') == getCookie('menuIndex')) {
		$(this).show();
	} else { // Esconde o restante
		$(this).hide();
	}
})

Eis o script completo:

$(document).ready(function() {				 
	/**
	 * Retorna o valor do cookie
	 * @autor Deusimar Ferreira 
	 * @param string name
	 */
	getCookie = function(Name){ 
		var re=new RegExp(Name+"=[^;]+", "i");
		if (document.cookie.match(re))
			return document.cookie.match(re)[0].split("=")[1];
		return null;
	}
	
	/**
	 * Define cookie
	 * @autor Deusimar Ferreira 
	 * @param string name
	 * @param string|int value
	 */
	setCookie = function(name, value){
		document.cookie = name + "=" + value;
	}
	
	/**
	 * Accordion Menu
	 * @autor Deusimar Ferreira 
	 * @param #menu li ul
	 * @param #menu li div a
	 */
	if (typeof expandeIndices == 'string')
		expandeIndices = expandeIndices.replace(/c/ig, '').split(',') // Transfoma string value em array (ie: "c1,c2,c3" becomes [1,2,3]
	
	// Pega submenus 
	var subContents = $('#menu li ul');
	
	// Percorre o menu e adiciona indice
	$("#menu li div a").each(function(index) {
		subContents.eq(index).attr({menuIndex: index + 'c'});
	})
	
	// Apresenta apenas o primeiro menu list
	$('#menu li ul:not(:first)').hide();
	$("div a").click(function() {
		//$("#menu li ul:visible").slideUp("slow"); // Esocnde menu item que visivel
		//$(this).parent().next().slideToggle("slow"); // Mostra o menu item que recebeu o evento click		
		
		// Criar objeto gobal do parent span a 
		var _this = $(this).parent().next();
		
		// Percorre o menu para verificar as ações que deve ser executada
		// Verifica qual submenu deve ser fechado e qual será aberto
		$("div a").each(function(index){
			if ($('#menu li ul:eq(' + index + ')').attr('menuIndex') != _this.attr('menuIndex')) {
				// Verifica se exite algum submenu que não seja o que recebeu o evento do click aberto
				if ($('#menu li ul:eq(' + index + ')').is(':visible')) 
					$('#menu li ul:eq(' + index + ')').slideUp("slow"); // Esocnde submenu item que visivel
			} else {				
				(_this.is(':visible')) ? _this.slideUp("slow") // Esocnde submenu item que visivel
									   : _this.slideDown("slow"); // Mostra o submenu item que recebeu o evento click
			}
		})
		
		// Retorna falso funciona como um pause
		return false;
	})		
	
	// Evento window unload
	$(window).bind('unload', function(){
		$('#menu li ul').unbind(); // Remove eventos
		var expandeIndices = []; // Array indice
		
		$("#menu li ul:visible").each(function(index){ 
			// Insere o valor do menuindex no array
			expandeIndices.push($(this).attr('menuIndex'));
		})
		
		expandeIndices = (expandeIndices.length == 0) ? '-1c' : expandeIndices;
		setCookie('menuIndex', expandeIndices); // Seta o cookie e seu respectivo valor
	})		
	
	// Verifica o cookie para ver qual submenu deve ser mostrado
	$("#menu li ul").each(function(){
		if ($(this).attr('menuIndex') == getCookie('menuIndex')) { // Mostra o submenu
			$(this).show();
		} else { // Esconde o restante
			$(this).hide();
		}
	})
})

Folha de estilo css:

div {
	font-family: Verdana, Arial;
	font-size: 11px;
	font-weight: bold;
	margin: 1px 0px;
	padding: 0px;
	border: 1px #06c solid;
	background-color: gray;
}

ul {
	list-style-type: none;
	max-width: 202px;
	_width: 200px; /* Adivinha para quem serve essa linha - Claro que é o IE! */
}

#menu li {
	margin: 0px 0px;
}

#menu li ul{
	margin: 0px;
	padding: 1px 5px;
	display: none;
}

#menu li div a{				
	text-decoration: none;
	outline: none;
	line-height: 18px;
	padding: 1px 4px;
	color: white;
}

a {				
	font-family: Verdana, Arial;
	font-size: 11px;
	text-decoration: none;
	outline: none;
	padding: 1px 4px;
	color: #06c;
}

a:hover {				
	font-family: Verdana, Arial;
	font-size: 11px;
	text-decoration: underline;
	outline: none;
	padding: 1px 4px;
	color: #06c;
}

Estrutura do menu em lista não ordenada, ul li:

<ul id="menu">
	<li>
		<div><a href="">JQuery Documentation</a></div>
		<ul id="submenu">
			<li><a href="http://docs.jquery.com/Main_Page" target="blank" title="Documentation">Documentation</a></li>
			<li><a href="http://docs.jquery.com/Tutorials" target="blank" title="Tutrorials">Tutrorials</a></li>
		</ul>
	</li>
	
	
	<li>
		<div><a href="">Player Game</a></div>
		<ul id="submenu">
			<li><a href="http://www.cdt.unb.br/memoria" title="Memória Empreendedora">Memória Empreendedora</a></li>
			<li><a href="" title="Memória Empreendedora">Memória Empreendedora</a></li>
		</ul>
	</li>
	
	<li>
		<div><a href="">Sobre</a></div>
		<ul id="submenu" style="border-bottom: 1px #06c solid;">
			<li><a href="https://zimar.wordpress.com/about/" title="Sobre Me">Sobre Me</a></li>
			<li><a href="http://docs.google.com/View?docid=ddft82q4_42hmpmmcgv" title="Curriculum">By Curriculum</a></li>
		</ul>
	</li>
</ul>

é isso ai, qualquer dúvida, pergunte!