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!


Ações

Information

16 responses

28 07 2008
Marcelo

Tem como fazer isso funcionar para as categorias com as subcategorias do WordPress? Tentei de tudo quanto é jeito e não consegui. Procurei plugins e encontrei pouca coisa mas nenhum q funcione, queria somente para fazer o accordion nas categorias e subcategorias…

Vlw!

30 08 2008
zimar™

Qual a versão do wordpress você esta usando, até o momento não tinha testado no wordpress, vou fazer um exemplo para ver como que funfa, depois posto o resultado aqui.

mto obrigado pela participação, t+.

20 12 2008
Ricardo Brazileiro

Opa, parabens pelo artigo.
Cara, eu tava querendo fazer uma modificação para uma adequação ao wordpress e não to conseguindo achar as alterações necessárias no código.

Como eu faço para usar o para separar quem é menu e submenu no código do jQuery?

Seria isso ?
var subContents = $(‘#submenu ul’);
$(“#menu li div a”).each(function(index) {
subContents.eq(index).attr({menuIndex: index + ‘c’});
})

To na dúvida…

Abs

29 12 2008
zimar™

Olá, Ricardo,
Vc esta querendo serpar o menu do submenu ou apenas pegar os submenus, para pegar o submenu estou utilizando o código abaixo.

// Pega submenus
var subContents = $(‘#menu li ul’);

27 12 2008
João F. Melo

Também tenho a mesma dúvida do Marcelo.
Mas fora esta sabe me dizer como faço um menu horizontal que fique marcado na categoria escolhida?

29 12 2008
zimar™

Boa tarde, João,
Para o menu horizontal lhe recomendo utilizar o método $.tabs() do JQuery, dê uma olhada nos links abaixo.

http://docs.jquery.com/UI/Tabs
http://www.gmarwaha.com/blog/?p=7

2 01 2009
rbrazileiro

opa zimar, valeu!
então, surgiu outra dúvida:
como eu faço pra ativar os links nos indices? tem como?
até então ele só faz abrir e fechar os submenus né?
seria mexer no $(“div a”).click(function() ?

abs
🙂

4 01 2009
zimar™

Realmente não tem este tratamento, mais segue uma sugestão para vc tentar resolver, seguite no href do indice vc insere o link que deseja abrir e dentro do método $( “div a” ).each(function(index) {} ) você faz algo como window.location = $( “div a” ).attr( “href” );

Ex:
$( “div a” ).click( function() {
$(“div a”).each(function(index){

window.location = $( “div a” ).attr( “href” )
}
} )

Ou você também pode retirar o return false; do método $( “div a” ).click( function() {} ).

abs!

1 07 2009
Lúcio Kleber

Esse menu aiu como uma luva para um projeto que estava atrasado e não tinha-se tempo de bolar um desses.

Voilá

27 07 2009
David

Teria como criar mais um submenu?
A estrutura seria
– Menu
*-links
– Submenu
*-links
– Submenu 2
*-links

Eu tenteii mais não consegui!

28 07 2009
zimar™

Olá, David,

é possivel criar esta estrutura, mais será necessario repensar toda estrutura do menu.

28 07 2009
David

Você tem interessem em nos ensinar???
Obrigado

4 08 2009
zimar™

Olá, David,
no momento estou sem tempo para lhes ensinar, provavelmente só vou conseguir depois do mês de setembo.

mais por enquando, lhe recomendo dar uma olhada na documentação do JQuery.

13 08 2009
Paulo

Olá Zimar… gostaria de tirar uma dúvida de principiante… to usando o seu menú e tá funcionando direitinho… mas gostaria de usá-lo da mesma forma q o colega disse um pouco acima… com o link no índice. Tentei praticar a sua sugestão mas não conseguí… tem como vc colocar o script já com a inserção q vc citou? Obrigado…

21 08 2009
zimar™

Fala Paulo, blz,

Puts, tá hospitalizado por alguns dias, mais já estou desenvolvendo um exemplo de submenu com link, assim que finalizar posto aqui.

abçs!

26 08 2009
zimar™

Bem pessoal, segue a solução.

Primeiro: Faça um pequeno ajuste no js, coloque o bloco da linha 49 à 63 dentro de um verificador, segue exemplo abaixo.

if( $( _this ).attr("id") == "submenu" ) {
	// 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;
}

Segundo: Para inserir link no menu basta alterar a estrutura da lista removendo o ul “submenu”, observe o exemplo abaixo.

<ul id="menu">
	<li>
		<div><a href="http://docs.jquery.com/">JQuery Documentation</a></div>
	</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>
	<li>
		<div><a href="http://www.google.com.br">Google</a></div>
	</li>
</ul>

abrçs, espero ter ajudado, qualquer dúvida, tó por aqui!

Deixe um comentário