Menú vertical en acordeón con CSS y jquery

menu_acordeonA pesar del buen feedback recibido sobre mi serie de tutoriales de CodeIgniter, los artículos sobre menús animados con CSS y javascript siguen siendo los más populares en este sitio, y parece que son bastante útiles para la gente que está empezando. Por ello hoy me tomo un pequeño respiro de los videotutoriales y voy a enseñaros cómo hacer el clásico menú vertical en acordeón con CSS y jQuery.
El resultado lo puedes ver aquí: Ver demo

HTML

Para el html del menú vamos a emplear la estructura típica de listas anidadas. La última entrada será un enlace directo, sin submenú:

<ul id="menu">
<li><a href="#">Menu 1</a>
	<ul>
		<li><a href="#">Submenu 1</a></li>
		<li><a href="#">Submenu 2</a></li>
		<li><a href="#">Submenu 3</a></li>
		<li><a href="#">Submenu 4</a></li>
	</ul>
</li>
<li><a href="#">Menu 2</a>
	<ul>
		<li><a href="#">Submenu 1</a></li>
		<li><a href="#">Submenu 2</a></li>
		<li><a href="#">Submenu 3</a></li>
		<li><a href="#">Submenu 4</a></li>
	</ul>
</li>
<li><a href="#">Menu 3</a>
	<ul>
		<li><a href="#">Submenu 1</a></li>
		<li><a href="#">Submenu 2</a></li>
		<li><a href="#">Submenu 3</a></li>
		<li><a href="#">Submenu 4</a></li>
	</ul>
</li>
<li><a href="#">Menu sin submenu</a></li>
</ul>

Añadiendo el CSS

Para dar estilo al menú tenemos primero que quitar los estilos por defecto de las listas, eliminar los bullets y márgenes y añadirle display:block para que queden perfectamente alineados los elementos uno debajo de otro. Después tenemos que ocultar con display:none los submenús para que aparezcan todos colapsados por defecto. Vamos a añadir algunas extensiones de CSS3 para mejorar visualmente nuestro menú. Estás extensiones sólo serán visibles en navegadores modernos (Firefox, Chrome y Safari), pero no en Internet Explorer. De esta manera podemos añadir bordes redondeados y sombras al menú y al texto. La propiedad -webkit-transition solo funciona en navegadores basados en webkit (Safari y Chrome), y la utilizaremos para mejor el hover añadiendo un fundido en el color del texto y el background. En los navegadores que no soporten estas propiedades simplemente veremos el menú con esquinas normales, hover típico on-off de CSS y sin sombras.

#menu{
	-moz-border-radius:5px;
	-webkit-border-radius:5px;
	border-radius:5px;
	-webkit-box-shadow:1px 1px 3px #888;
	-moz-box-shadow:1px 1px 3px #888;
}
#menu li{border-bottom:1px solid #FFF;}
#menu ul li, #menu li:last-child{border:none}	
a{
	display:block;
	color:#FFF;
	text-decoration:none;
	font-family:'Helvetica', Arial, sans-serif;
	font-size:13px;
	padding:3px 5px;
	text-shadow:1px 1px 1px #325179;
}
#menu a:hover{
	color:#F9B855;
	-webkit-transition: color 0.2s linear;
}
#menu ul a{background-color:#6594D1;}
#menu ul a:hover{
	background-color:#FFF;
	color:#2961A9;
	text-shadow:none;
	-webkit-transition: color, background-color 0.2s linear;
}
ul{
	display:block;
	background-color:#2961A9;
	margin:0;
	padding:0;
	width:130px;
	list-style:none;
}
#menu ul{background-color:#6594D1;}
#menu li ul {display:none;}

Añadiendo la funcionalidad con jQuery

Ahora vamos a ver lo sencillo que es hacer funcionar nuestro menú vertical. Primero añadimos el evento click a cada enlace del menú. Después comprobamos si el siguiente elemento tras el enlace es un ul, ya que si es así este contendrá un submenú, si no será un elemento de un submenú o bien un elemento principal que no contiene submenú (en nuestro ejemplo, el último enlace). Si contiene un submenú, al hacer click este se expandirá o colapsará (slideToggle()), a la vez que colapsaremos el submenú que esté visible que no sea el actual. La función event.preventDefault() sirve para evitar que cuando hagamos click el navegador siga el enlace del href tras ejecutar nuestra función javascript.

<script type="text/javascript" charset="utf-8">
$(function(){
	$('#menu li a').click(function(event){
		var elem = $(this).next();
		if(elem.is('ul')){
			event.preventDefault();
			$('#menu ul:visible').not(elem).slideUp();
			elem.slideToggle();
		}
	});
});
</script>

Podéis escribir en los comentarios cualquier duda que tengáis e intentaré responderla, así como sugerencias para próximos tutoriales.

Actualización: Ante la cantidad de peticiones de cómo hacer que el menú sea multinivel, decidí convertirlo en un plugin de jQuery y hacer un pequeño tutorial con screencast incluido: Cómo hacer un plugin jQuery – Menú acordeón multinivel

Enlaces

183 comentarios para “Menú vertical en acordeón con CSS y jquery”

  1. Bral dice:

    hola David, te queria preguntar que en vez de un texto en el boton saliera una imagen.

    Lo consegui con otro tipo de menu pero con este no.

    Gracias.

  2. Bral dice:

    vale, ya esta hecho.

    xD

  3. Alejandro dice:

    Quería dejarles para que lo chequearan, un menú horizontal tipo acordeón con sólo CSS. También he desarrollado una versión con el efecto deslizante que utiliza SMIL (HTML+TIME) para IE y «CSS3 transitions» para el resto.

    Enlace: http://alejandroaraneda.blogspot.com/

  4. Bral dice:

    queria hacer otra pregunta.

    como se haria para que en lugar de cerrarse el menu clicar sobre otra seccion, se quedaran todas abiertas?

    no se si me explico.

  5. David Rojas dice:

    @Bral Simplemente quita la línea que pone $('#menu ul:visible').not(elem).slideUp();. Es fácil de entender, esta línea busca los elementos visibles que sean distintos del que acabas de clickar y lo oculta.

  6. Bral dice:

    si es lo que habia pensado, gracias por confirmalo.

    saludos

  7. augusto dice:

    como se puede hacer para agregras un nivel mas de sub menu y que se desplige sin que se cierre el menu

  8. David Rojas dice:

    @augusto Eso lo tengo preparado para otro artículo cuando tenga tiempo, y de paso aprovecharlo para explicar cómo hacer un plugin en jQuery 🙂

  9. augusto dice:

    por favor me podrias espicar ya que lo necesito urgente

    please!!!!!

  10. alfonso dice:

    hola
    he seguido esta explicacion me parece muy bien, pero no se como relacionar el codigo de la funcion javascript con el html y css, disculpa la pregunta pero estoy aprendiendo, me puedes ayudar con ello.

  11. David Rojas dice:

    @alfonso Carga la demo y mira el código fuente, ahí veras donde se tiene que colocar todo.

  12. Christian dice:

    Hola David, gracias por compartir tus codigos, queria saber si podrias explicarme como agregar un nivel mas de sub-menu, eh estado intentando pero no he logrado obtener el resultado esperado …. gracias

  13. alberto dice:

    Buenos dias David probando lo del menu no me funciono nose porque es decir lo puse igual que el suyo pero no se me abren las pestañas aver si me pudiera dar alguna sugerencia gracias

  14. Jon dice:

    Hola, antes que nada te quiero agradecer el post, esta muy bien la explicación y sobre todo el codigo.

    El unico detalle es que cambia mucho el layout del menu en IE6… ¿Algún consejo?

  15. David Rojas dice:

    @Christian Estoy en ello, en cuanto pueda pondré otro artículo explicándolo, paciencia 🙂
    @alberto Si no me das más datos… Prueba instalando Firebug y mira en la consola si te da algún error.
    @Jon IE6 no soporta por ejemplo el child selector (‘>’) del css, puedes buscar alguna manera creativa de sustituirlo. Contra los bordes redondeados, sombras, etc. no hay nada que hacer (en ninguna versión de IE). Personalmente he dejado de soporta IE6, google oficialmente deja de soportarlo para youtube y google docs este mes, y posteriormente lo hará para gmail. No vale la pena, y gracias a que google abandona su soporte seguramente veremos que en los próximos meses el uso de IE6 tiende a desaparecer rápidamente.

  16. Gon dice:

    Muy buena tu ayuda David, espero nos puedas ayudar para aumentar un nivel mas, estoy intentando y probando y no me sale espero tu colaboracion. Muchas gracias…

  17. Mar dice:

    Hola David, muchas gracias ante todo 🙂
    Mi idea sería hacer esto mismo, pero sin submenus. Que cuando pinchase en el primer nivel se despliegue contenido de texto.
    Por ejemplo, una lista de ciudades y que al pinchar en cada una apareciese información sobre la ciudad.
    No se si me he explicado… me podrías ayudar??
    Gracias

  18. David Rojas dice:

    @Mar Funciona exactamente igual. Donde pones un link a submenú, en vez de poner ese enlace pones tu contenido. Por lo tanto por ejemplo dentro de «menú 1» tendrás una lista con un ul y un solo li con su contenido dentro (pero ningún enlace).

  19. juan camilo hoyos dice:

    David gracias por el menui, me parece supuer chulo, pero cuando coloco el codigo CSS en mi Blog y luego pego el mismo HTML me queda un listado simplemente, no ponde ningun diseño ni nada mas, podrias asesorarme.
    Gracias juanchoyos@gmail.com

  20. Z4 dice:

    Hola! muuuy bueno el menu, david. Yo tb me llamo David :D. A ver te explico, kiero utilizar el menu para k cada elemento del submenu me introduzca mediante isiajax un contenido diferente en un div.. pero n o me funcionan los submenus al activar isiajax en el head… por k sera?? me puedes ayudar?¿?

  21. David Rojas dice:

    @Z4 Ni idea… no he usado isiajax, y si no pones el código no sé que te puede fallar.

  22. Sebastian Camilo dice:

    Heeeeee david super tu menu pero me gustaria como hacer para que una vez seleccionado se quede desplegado
    Graxx

  23. David Rojas dice:

    @Sebastian Eso tendrás que hacerlo mediante PHP, añadiendo una clase para que permanezca visible. Con javascript no puedes hacerlo ya que al pinchar en un enlace, el estado creado se «pierde» al cargar una página nueva.

  24. Alfred dice:

    Hola!!

    Muy bueno tu blog…!!! me ha ayudado a entender codeigniter mejor!!
    Tengo un problema, el menu me sirve perfecto cuando hago llamado de este como una pagina html normal, o cuando hago el llamado a través de un controlador con load view, pero estoy utilizando templates y cuando hago el llamado del menu en una controlador a través de una de las variables del template ($menu) lo muestra en la vista del template pero al seleccionar un nivel se retrae y desaparece… alguna idea??

  25. David Rojas dice:

    @Alfred Sin ver el código, no lo sé. Pero el menú debe ser independiente de lo que programes en el backend. Mira el fuente html que te genera tu template (me refiero a «ver código fuente» en tu navegador), porque quizá no está cargando el script o estás metiendo código de más.

  26. enzo dice:

    hola, muy bueno el menu, una consulta jquery se puede usar asi libremente.

  27. David Rojas dice:

    @enzo ¿A qué te refieres con libremente? Si es a la licencia de uso, jQuery es totalmente libre: http://jquery.org/license

  28. enzo dice:

    otra consulta, sabes como se puede contraer el menu al pinchar en un item principal que no tenga submenu?. de ante mano gracias.

  29. David Rojas dice:

    @enzo No te entiendo, si no tiene submenú no tiene nada dentro que se pueda contraer.

  30. enzo dice:

    es cuando pincho en un item con submenu se despliega ese submenu, luego, sin contraer, pincho en un item sin submenu, el submenu desplegado anteriormente sigue desplegado y mi intencion es que se contraiga el submenu que esta desplegado.

  31. David Rojas dice:

    @enzo Por lo que dices supongo que cargas el contenido con ajax. Si es así en tu llamada ajax solo tienes que poner $('#menu ul').slideUp() tras cargar el contenido, para de esta manera cerrar los submenús visibles.

  32. Hola David, he utilizado el menu en una página construida con divs: un div id=»container» y dentro de este otros divs: div id=»sidebar1″ y div id=»mainContent». El menú está dentro de sidebar1 y quiero que cuando clique en los submenus estos me carguen una página html en el div mainContent. ¿Cómo puedo hacerlo? He mirado por ahí y he probado varias cosas pero ninguna funciona…
    Muchas gracias.
    Un saludo

  33. David Rojas dice:

    @Patricia puedes hacerlo con javascript usando ajax, o usando un iframe. El iframe no es recomendable. Investiga por aquí: http://api.jquery.com/load/

  34. Hola

    Te agradezco el subir un recurso como éste: estaba buscando algo parecido y lo encontré relativamente rápido.

    Una felicitación y espero todo ande bien.

    Saludos

  35. Santiago dice:

    David mis verdaderas felicitaciones este menú combina delicadeza con estilo mas un toque de efectos… es lo que andaba buscando para mi sistema desde ya muchas gracias cuando lo tenga listo te paso el link para que te des una vuelta, estos son mis primeros pasos en php,jquery,html.
    Saludos

  36. Borja dice:

    Hola David;
    Mira estoy haciendo un proyecto con una aplicacion web en la que busca datos en google y los guardo en una lista y quisiera mostrar esos datos en un acordeon. Por ejemplo una busqueda de una planta y que me mostrara dentro del acordeon una foto, una descripcion y un enlace de donde se encontro la noticia.Estoy buscando la manera de hacer que me muestre por ahora la informacion dentro del acordeon pero se me escapa!! AYUDA?
    Muchas gracias!!

  37. Irene dice:

    Buff!!!

    He tenido suerte al dar con tu blog, llevo un día y medio buceando por internet en busca de un tesoro como este.

    Una explicación genial!!!!! ahora ya me voy enterando un poco más del JavaScript…

    Muchas gracias por compartir tus conocimientos!!!!!

    Salut!!!

  38. Josh dice:

    A mí me pasa algo que me trae ya loco.

    Si el menú lo hago con html estático me lo hace correctamente.

    Si los elementos los pongo con un bucle en php me muestra todos los elementos con los colores bien pero sin la funcionalidad de pinchar y que se oculten y se abran.

    ¿Qué puede ser?

    Pd: para que veas lo curioso que es, pongo los dos menús (html y php) y si pulso en el menú generado en html plano, los componentes del menú que ha sido generado con php se repliegan solos :S pero si intento pulsar en uno de estos elementos del menú en php me recarga la página y me despliega todas las categorías principales con todos sus subcategorías.

  39. David Rojas dice:

    @Josh Me temo que si no me pones alguna url con el ejemplo de lo que te pasa, no puedo ayudarte.

  40. Omar dice:

    Hola David, primero congrats por tu web me ha sido de mucha ayuda, lo otro que tengo una web y tengo el mismo problema que @enzo xD

    lo que pasa es que no se como hacer que cuando se despliege un submenu se cierre al hacer click en otro boton que no tenga sub menu, muchas gracias >D

  41. Daerun dice:

    Hola, este código me resultó muy útil, pero el IE me está fastidiando y mucho, y es que el menú no se expande correctamente: le he puesto un overflow:visible, y resulta que lo que hace es desplegarse pero superponiendose a la altura del menu, sin aumentar la altura del contenedor, no sé si me explico.

  42. Daniel dice:

    Hola David vuelvo a escribirte, parece que no llego a tu blog mi mensaje.
    probe tu menu y esta bueno, pero me pasa que IE los subMenus salen muy separados uno del otro…

    en Chrome sale ok.
    Menu
    SubMenu1
    SubMen2
    SubMenu3

    en Internet Explorer me Sale asi.
    Menu
    Submenu1

    SubMenu2

    SubMen3

    ves estan muy separados uno del otro.. hay alguna propiedad para que se mantegan juntos..
    y quisiera poner una imagen antes del texto como lo haria….
    algo asi.
    (imagen)Mensajes

    gracias…. felicitaciones por tu blog. 🙂

  43. David Rojas dice:

    @Daniel Juega con el margin y padding de los <li> a ver que tal.

  44. Daniel dice:

    Hola David Gracias por responder.. ya solucione ese problema del espacio entre los subMenus, como? tenia el IE6 asi ke actualize mi navegador IE8 y todo perfect. ahora deseo poner una imagen o icono al lado del nombre del Menu…algo asi
    (imagen)Menu1
    (imagen)Menu2

    tal como esta en esta pagina de ejemplo.

    http://infectedfx.net/wp-content/ejemplos/ajaxflagmenu/

    ahi usa Div, algo que io no uso trabajo con asp.net.. solo deseo poner la imagen a tu menu Acordeon Como lo hago david?
    Gracias por tu tiempo.

  45. David Rojas dice:

    @Daniel No entiendo lo de que usa divs y tu asp, no tiene nada que ver una cosa con la otra. Te recomiendo te leas algún buen manual de html y css, pero de todas formas lo que quieres puedes hacerlo añadiendo en el css un background-image con el icono a cada link, y ponerle algo de padding a la izquierda para que el texto del link no quede solapado con la imagen, no sé si ves la idea.

  46. daniel dice:

    David, queria felicitarte por compartir tus recursos y conocimientos con nosotros, aprovecho la ocasión para preguntarte que valores debo quitar o modificar para que en IE (Lo he probado en IE 7 Y 8)los submenues salgan menos separados, en mozilla y demas exploradores sale bien pero en el IE salen como te lo menciono, no importa que tenga que quitarle el efecto de sombra. gracias

  47. David Rojas dice:

    @daniel Ve modificando el margin y padding de los elementos, probablemente IE está metiendo algunos valores por defecto en las listas.

  48. tony dice:

    Hola, muy buen blog, y el menú está excelente, me gustaria que los enlaces internos del menu fuesen en php para hacer las llamadas y no en ajax, me podrias hechar una mano?
    Saludos y gracias.

  49. Lucas dice:

    Hola! excelente el menu, lo estoy adaptando y me da muy buen resultado..

    mi pregunta: como hago para que cuando cargue la pagina se abra por default alguno de los menu ? en el caso del demo me gustaria por ejemplo que inicialice con el menu 2 desplegado…

    Muchas Gracias!

Deja un comentario

Time limit is exhausted. Please reload the CAPTCHA.

RSS iTunes podcast Twitter

Categorías

Enlaces

Archivos