Menú acordeón. Haciéndolo simple con jQuery | Oloblogger jQuery es una biblioteca JavaScript que ya casi se está convirtiendo en un estándar. Nacida en 2006, parece que actualmente es con diferenci...

10 de julio de 2014

Menú acordeón. Haciéndolo simple con jQuery

jQuery es una biblioteca JavaScript que ya casi se está convirtiendo en un estándar. Nacida en 2006, parece que actualmente es con diferencia la más usada y su principal ventaja es que nos hace más simples la manera de manejar los documentos HTML y la manipulación del DOM, permitiéndonos modificar el contenido de una página sin que sea siquiera necesario recargarla.

Lo que veremos hoy se aprovecha de todo esto para transformar un simple par de listas anidadas en un menú tipo "acordeón", esos en los que se despliegan las sub-opciones al tiempo que se pliegan las que pudieran estar previamente abiertas. Lo podéis ver funcionando al final de esta misma entrada o en este Codepen.



Todo esto que viene a continuación lo he puesto por separado para poder ir explicándolo un poco, pero se puede copiar y pegar dentro de un gadget, un trozo tras otro -en el mismo orden en que se presentan- y funcionará sin problemas.


HTML


La lista se construye con normalidad con la etiqueta ul y sucesivos li. Todo lo meteremos dentro de una caja con una clase que he llamado menujq para poder manejar el estilo de esta lista de manera independiente a otras listas comunes que pudieran existir en la misma página.

Para las sub-opciones simplemente se anidará otra lista completa dentro de un elemento li, teniendo cuidado de cerrar ese elemento tras el cierre de la lista anidada (</ul>).

Los enlaces que servirán para desplegar no pueden llevar a ningún sitio porque no cumplirían su función, así que se rellenan con un javascript:void(); y listo.

<div class="menujq">
<ul>
 <li><a href="javascript:void();">Opción1 desplegable</a>
  <ul>
   <li><a href="URL del enlace">Opc. 1.1</a></li>
   <li><a href="URL del enlace">Opc. 1.2</a></li>
  </ul>
 </li>
 <li><a href="javascript:void();">Opción2 desplegable</a>
  <ul>
   <li><a href="URL del enlace">Opc. 2.1</a></li>
   <li><a href="URL del enlace">Opc. 2.2</a></li>
   <li><a href="URL del enlace">Opc. 2.3</a></li>
  </ul>
 </li>
 <li><a href="URL del enlace">Opción3 Directa</a></li>
</ul>
</div>

CSS


La parte del estilo, además de servirnos para configurar los tamaños y para conseguir diferenciar con color la lista principal de las anidadas, también la hemos aprovechado para añadir unos simbolitos que diferencien si el elemento es desplegable o no. Detallado queda en los comentarios.

<style>
.menujq ul {
list-style: none;
width: 80%;
margin: 0 auto;
padding: 0;
}
.menujq a {
display: block;
padding: 10px;
border-bottom: 1px solid #fff;
background: #D25400;
color: #fff;
text-decoration: none;
font-size: 16px;
line-height: 16px;
text-transform: uppercase;
}
/* Símbolo elemento normal */
.menujq ul li a:before {
content: "\25CF\00A0 ";
width: 28px;
display: inline-block;
vertical-align: top;
}
/* Símbolo elemento desplegable cerrado */
.menujq ul li.desplegable a:before {
content: "\25BA\00A0";
}
/* Símbolo elemento desplegable abierto */
.menujq ul li.desplegable.activa a:before {
content: "\25BC\00A0 ";
}
/* Eliminar símbolos para sub-opciones */
.menujq ul li.desplegable ul li a:before,
.menujq ul li.desplegable.activa ul li a:before {
content: "";
}
/* Lista anidada inicialmente oculta */
.menujq ul ul {
display: none;
width: 100%;
}
/* Sangrado y segundo color para sub-opciones */
.menujq ul ul a {
padding-left: 20px;
background: #E77E23;
text-transform: capitalize;
}
</style>

JAVASCRIPT


Y por fin el alma de todo esto, el JavaScript hilvanado con jQuery.

Básicamente lo que hace, en este orden, es:

  • Buscar los elementos de lista, hijos directos de la clase menujq cuyo hijo sea una etiqueta de lista ul y añadirle una clase que he llamado desplegable. En otras palabras, localizar y marcar los elementos de lista que contienen una lista anidada dentro de ellos y que serán los únicos que se podrán desplegar.
  • Controlar el clic sobre cualquier enlace dentro de las listas
  • Guardar el objeto que sigue tras aquel sobre el que se ha hecho clic (comprobar)
  • Limpiar todas las clases activa que se hayan podido añadir en anteriores clics...
  • ...y añadírsela al elemento li que contiene el enlace sobre el que se hecho clic ahora
  • Las dos últimas condiciones sirven para ver si una lista está plegada o desplegada (:visible) para añadirle el efecto de plegado (.slideUp) o desplegado (.slideDown).


<script type="text/javascript">
$(document).ready(function(){
$('.menujq > ul > li:has(ul)').addClass('desplegable');
$('.menujq > ul > li > a').click(function() {
var comprobar = $(this).next();
$('.menujq li').removeClass('activa');
$(this).closest('li').addClass('activa');
if((comprobar.is('ul')) && (comprobar.is(':visible'))) {
$(this).closest('li').removeClass('activa');
comprobar.slideUp('normal');
}
if((comprobar.is('ul')) && (!comprobar.is(':visible'))) {
$('.menujq ul ul:visible').slideUp('normal');
comprobar.slideDown('normal');
}
});
});
</script>


Y así es como quedará todo junto:



Ah, y casi se me olvida por obvio. Si queremos que esto funcione tenemos que tener la librería jQuery en nuestra plantilla. Si no es así entonces delante de todo este código o en la plantilla -si es que pensamos usarla para otros asuntos futuros- antes del cierre </head> tendremos que insertar esto:

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js" type="text/javascript"></script>

¿Vemos otro post al azar por si le encuentras utilidad o quizás prefieres ser más metódico y suscribirte a nuestras entradas por correo? También puedes imprimir este artículo y por supuesto compartirlo en redes sociales si fue de tu agrado.

Compartir
Copy URL

Y muchos más artículos interesantes si nos sigues en...

follow us in feedly

63 comentarios :

  1. Muy interesante el post Oloman. De seguro a más de uno esto le va caer como anillo al dedo. Saludos

    ResponderEliminar
  2. El mejor post que he encontrado sobre el tema, mil gracias!!

    ResponderEliminar
  3. Tenía otro idéntico pero NO era una lista, y cada vez que le agregaba una entrada, se cambiaban las --p-- por --div-- y ya no me funcionaba y tenía que volver a configurar de nuevo. Espero que con este no me ocurra lo mismo, de momento funciona, solo que se desplaza a la derecha los enlaces, por favor dime si puedo corregirlo. ¡¡Gracias!! te dejo el enlace al blog. http://doshermanasayeryhoy.blogspot.com.es/

    ResponderEliminar
    Respuestas
    1. Prueba a añadir al CSS esto:
      .widget .menujq ul ul {
      padding: 0;
      }

      Eliminar
    2. estimado no vi cambio con ese agregado. q deveria ver? como limito el ancho de todo el menu? gracias

      Eliminar
    3. Hola Ariel ¿qué quieres hacer exactamente? ¿Cuál es tu blog?

      Eliminar
    4. Hola como estas? queria ahcer una emnud esplegable eso es. ya limite el ancho lo que no se como ahcer es que no despleque cuando no tiene hijos el menu principal. lo que no note es ningun cambio cuando agregue esto:
      .widget .menujq ul ul {
      padding: 0;
      }
      que deberia ver? no tengo un blog. gracias

      Eliminar
    5. Con este código ese tema es automático. No tienes que añadir nada. Simplemente, los enlaces que no tienen hijos serán como ese del ejemplo en el que puse "enlace directo" y el HREF en lugar de javascript:void(); tendrás que poner la dirección de destino:
      <li><a href="URL del enlace">Opción3 Directa</a></li>

      Eliminar
  4. Perfecto lo he añadido al final del CSS, y funciona de maravilla. Eres un "artista" muchas GRACIAS de nuevo.

    ResponderEliminar
  5. Excelente este post como todos los demas Oloman un saludo !!!!

    ResponderEliminar
  6. Genial olo que detalle ,como me encanta aprender eres un artista, eh visto cosas que me imaginaba pero voy dándole un repaso . Un saludo

    ResponderEliminar
  7. Muy bueno, sigo investigando y aprendiendo. Gracias

    ResponderEliminar
  8. Gracias por el aporte. Me gustaría poder hacer una pequeña variación: que al situar el puntero sobre cada item del menú principal, se desplegase el correspondiente submenú, sin clicar. Cómo podría hacerlo? Gracias

    ResponderEliminar
    Respuestas
    1. No sé seguro si va a funcionar porque ahora mismo voy a toda pastilla contestando comentarios atrasados y no tengo tiempo para probarlo, pero prueba tú a cambiar el .click del último trozo de código por un .mouseover

      Eliminar
  9. Gracias Oloman, no he podido probar porque tu código no me funciona, así tal y como está publicado. El Dreamweaver me dice que hay un error de sintaxis en la línea 3 del js, no sé si puede ser eso

    ResponderEliminar
  10. Era error mío, perdón, sin embargo no me funciona

    ResponderEliminar
    Respuestas
    1. Patrici4, el código que puse es correcto porque además de en CodePen, en esta entrada puedes comprobar que funciona. Seguramente es porque estás en Dreamweaver. Prueba en real.

      Eliminar
  11. Gracias Oloman, era correcto el .mouseover. Perfecto!

    ResponderEliminar
  12. HOla la verdad es que esta super genial este menú pero tengo un problema, cuando quiero desplegar la pestaña no me sale nada, quiero pensar que son por los códigos de script pero no estoy segura, a que se debe??

    ResponderEliminar
    Respuestas
    1. El código está bien Vicky, así que lo que te sugiero es que copies lo de esta entrada tal cual y una vez que compruebes que todo funciona correctamente en tu propia página, que entonces reestructures el HTML que forma el menú (primer trozo de código de este post) para que salga como tú necesitas.
      Ojo con el anidamiento de UL y LI, porque es fácil errar ahí.

      Eliminar
  13. Esta super tu menu pero como hago para mentener abierto el los submenus al seleccionar otro

    ResponderEliminar
    Respuestas
    1. Cambiando el JavaScript que puse por este otro:
      $(document).ready(function(){
      $('.menujq > ul > li:has(ul)').addClass('desplegable');
      $('.menujq > ul > li > a').click(function() {
      var comprobar = $(this).next();
      $(this).closest('li').addClass('activa');
      if((comprobar.is('ul')) && (comprobar.is(':visible'))) {
      $(this).closest('li').removeClass('activa');
      }
      $(this).next().slideToggle();
      });
      });

      Eliminar
  14. Una pregunta de principiante seguramente.... cómo tengo que nombrar el .js para que quede linkeado al html? Gracias de antemano..

    ResponderEliminar
    Respuestas
    1. Daniel, si tienes Blogger no necesitas nombrar la parte JavaScript ni enlazarla de ninguna manera. Directamente la copias y pegas justo antes del </head> de tu plantilla y funcionará sin problemas. En este post no expliqué dónde va cada parte de código que cito, pero es que ya lo digo en todos y de vez en cuando... descanso.

      Eliminar
  15. hola una consulta estos codigos se puede adaptar para paginas hechas con jimdo?

    muchas gracias por su respuesta...saludos.

    ResponderEliminar
    Respuestas
    1. Hola Willy. Se pueden colocar en cualquier tipo de sitio web. Sólo es cuestión de poner HTML, CSS y JavaScript, cada cual en su sitio. Yo indico dónde se inserta cada cosa en Blogger, pero en esa plataforma no te sé indicar.

      Eliminar
  16. bonito menú, Es posible agregar otro nivel

    ResponderEliminar
    Respuestas
    1. Sí, claro. En el enlace a Codepen del principio de esta entrada puedes ver este mismo código pero con un subnivel más.

      Eliminar
  17. Hola! me sirvió mucho el .js que permite dejar abierto el desplegable al seleccionar cualquiera de las OpcionX desplegable. Ahora, te hago una consulta, si quiero mantener también el desplegable abierto cuando selecciono cualquiera de las Opc x.x, que debería hacer? Necesito que el submenú despegable quede abierto en la opc que elija y no que se cierre al seleccionar uno de estos... se entiende?? espero me puedas ayudar xq estoy dando vueltas c esto hace mil años!!!

    ResponderEliminar
    Respuestas
    1. Un poco más arriba, en el comentario 13 y siguiente lo tienes Paine

      Eliminar
  18. Hola! cómo sería el script si quisiera tener un submenú abierto desde el principio?
    gracias

    ResponderEliminar
    Respuestas
    1. No hace falta que cambies el script. Lo que tendrías que hacer sería añadir al LI que quieres tener desplegado un class="active" y al UL que hay dentro de él un style="display:block;"

      Eliminar
  19. Buenas, no sé si los comentarios primero los tienes que aprobar para que aparezcan o no lo llegué a mandar bien el mensaje, por lo que por si acaso te mando de nuevo. Si he hecho mal, te pido disculpas ya que entiendo que a parte de esto también tendrás otras cosas.
    Tal y como puse en el comentario anterior, el código me funciona de lujo, pero me gustaría que el menú fuese responsive ya que si lo veo en el móvil, pues no sé ve bien, y quisiera hacerlo de esots menus que aparecen en el lateral, no sé si me explico.
    Pues un saludo y si he hecho mal en poner otro mensaje, este borralo y mis más sinceras disculpas

    ResponderEliminar
    Respuestas
    1. Aritz, este menú es "responsive", pues si la ventana del navegador es más pequeña, automáticamente se adapta a ese ancho.

      Pero por lo que entiendo, lo que tú quieres es que tamaños pequeños de ventana, el menú adopte otro formato. Eso ya es otra cosa y no esta. Se trataría de tener en realidad dos formatos de menú y según el ancho, mostrar uno u otro. Eso lo sé hacer de varias formas, pero ninguna de ellas la he explicado todavía en el blog. En alguna ocasión tocará...

      Eliminar
    2. Bueno eso más o menos, pero me interesa que se siga viendo con ese diseño en todo tipo de pantallas, aunque luego lo que sí que querría es que el menú salga por ejemplo por la derecha o izquierda, no sé si me explico. Ahora tengo el menú que en pantalla de pc lo abro, se despliega y todo bien, como lo uso en pantalla principal no hay scroll ni nada por el estilo por lo que hace lo que me interesa. Pero si me voy al móvil, al abrir el menú pues lo hace de igual forma pero al tener una lista larga, el footer que tengo se me va para abajo hasta que termina la lista, por lo que mi intención es que al pulsar para desplegar el menú, este salga por la izquierda o derecha y que el fondo se quede como está. Es decir, que haga más o menos la misma función que cuando creas un combobox con select option,
      Esto que te cuento entiendo que también pertenece a lo que comentas tú que aún no está escrito en el blog por lo estaré atento

      Eliminar
    3. ¿Quizás quieres algo como esto pero sólo para móvil?
      Pues viendo el código fuente puedes averiguar cómo lo hice.

      El problema que tengo a la hora de publicar el sistema es que sería complicado y largo de desarrollar en un post que lo explique de forma genérica y que sirva para todas las plantillas y eso es precisamente es lo que me propongo cuando publico algún artículo. Los casos particulares los trato de otra manera.

      Eliminar
  20. hola quisiera saber si es posible poner dentro de un acordeon, otro acordeon es decir en el panel del primer acordeon el contenido seria otro acordios hijo. Lohe intentado pero se me muestra solo el heder de los paneles del hijo y no se despliega. trabajo en asp.net c# y estoy usando controles de ajax toolkit, pero cualquier cosa que me de una idea pueda llevarla a lo mio, gracias de ante mano

    ResponderEliminar
    Respuestas
    1. Hola Orleni
      En este Codepen tienes un ejemplo de eso. Pincha en la opción 1 y luego en la 1.3

      Eliminar
  21. Hola. Sabes como puedo hacer que la lista de la opción desplegable permanezca abierta cuando selecciono un enlace?

    ResponderEliminar
    Respuestas
    1. Hola. Echa un vistazo al comentario 13 porque creo que es lo mismo que preguntas.

      Eliminar
    2. No. Lo ensayé y no es igual. Con este Javascript puedo tener todos los menús abiertos. Lo que busco es que cuando de click en el enlace del menú este no se cierre, pues termino perdida sin saber en que parte del blog estoy...

      Eliminar
    3. El enlace no se cierra Tortuga ¿quizás quieres decir que cuando pinchas en un enlace te cambia de página y entonces aparece el menú de nuevo cerrado?
      Va siendo el momento de que me digas en qué página lo tienes puesto para echar un vistazo y entender qué es lo que ocurre.

      Eliminar
    4. Si, es lo que pasa. Este es mi blog http://tortuga-taller.blogspot.com/

      Eliminar
    5. OK. Para que al cambiar de página (tras pinchar en un enlace del menú), las opciones se queden desplegadas tal y como estaban antes de saltar de página, sería necesario un sistema de cookies para que recordara la "última diposición" del menú para cada usuario. Sinceramente, no creo que merezca la pena cargar más de código la plantilla para eso, pero es que además me llevaría un post entero explicártelo.
      Si sabes manejar eso la idea al menos sí que te servirá.

      Eliminar
    6. Listo. Muchas gracias por responder...

      Eliminar
  22. Hola, como hago para cambiar el icono de la flechita (abierta y cerrada) por otro?

    ResponderEliminar
    Respuestas
    1. Hola. Eso lo tienes en esta parte del CSS:

      /* Símbolo elemento normal */
      .menujq ul li a:before {
      content: "\25CF\00A0 ";
      width: 28px;
      display: inline-block;
      vertical-align: top;
      }
      /* Símbolo elemento desplegable cerrado */
      .menujq ul li.desplegable a:before {
      content: "\25BA\00A0";
      }
      /* Símbolo elemento desplegable abierto */
      .menujq ul li.desplegable.activa a:before {
      content: "\25BC\00A0 ";
      }

      Sería cambiando lo que hay dentro de "content" por el código ISO del símbolo (entidad HTML) que quieras poner

      Eliminar
  23. Hola, tengo un problema con la librerías, tengo puesto un slider y al poner esta librería se me anula.
    ¿Alguna solución? Soy un poco novato en esto.

    Gracias

    ResponderEliminar
    Respuestas
    1. Hola Start. Prueba a usar jQuery en modo "noconflict". En este enlace tienes la referencia.

      Luego de eso has de sustituir todos los símbolos $ por la palabra jQuery.

      Eliminar
  24. Cómo podríamos hacer que se abran las pestañas pulsando en unos iconos que se encuentren arriba del acordeón? Y que cada uno de estos abra cada una de las secciones correspondientes

    ResponderEliminar
    Respuestas
    1. Hola Pedro. Eso sería similar a esto pero el desarrollo del código sería bastante distinto. Es decir, que habría que crear un código diferente.

      Eliminar
  25. Hola amigo! Habría alguna posibilidad de hacer esto mismo pero solo con CSS? Sin necesidad de Js o jQuery?

    ResponderEliminar
    Respuestas
    1. Tras la broma, tengo previsto publicar un post precisamente sobre ello. Será en unos días, pero no sé cuántos.

      Eliminar
  26. hola, disculpa, pero no me funciona lo que es el desplegable y creo que es por la etiqueta script , donde la coloco exactamente? tanto esa etiqueta como el javascript lo he colocado en la cabecera del tema , antes de cerrar el head, pero nada..

    ResponderEliminar
    Respuestas
    1. Hola Álvaro ¿quizás olvidaste poner la librería jQuery?

      Si teniéndola sigue sin funcionar, prueba con el JavaScript antes del </body>

      Eliminar
  27. Buenas Oloman!

    Al tratar de poner el JS me tira un error " Error al analizar XML, línea 1115, columna 26: The entity name must immediately follow the '&' in the entity reference." ¿A qué puede deberse?

    ResponderEliminar
    Respuestas
    1. A que a Blogger hay cosas que no le sientan bien.
      Cambia todos los & que veas en el código propuesto por &amp;

      Eliminar
  28. Mil gracias por este aporte, tenia muchos problemas para agregar otro tercer nivel

    ResponderEliminar
  29. Hola Oloman. He aplicado tal cual el código y excelente. Gracias. Por otro lado, cuando entro en un enlace y regreso atras, no me queda el menú tal cual inicié la aplicación, sino que me muestra en un listado todas las palabras (y sus palabras contenidas). Nuevamente gracias por todo. Un cordial saludo desde Mataró

    ResponderEliminar
    Respuestas
    1. No entiendo bien qué es lo que te pasa. Si me ofreces un enlace dónde verlo...

      Eliminar
  30. Hola como estás? lo realicé en un blog de prueba y me queda un ">" antes de cada despliegue, mi dirías como eliminarlo??
    te dejo el blog https://stlacadee.blogspot.com/p/entrenamiento.html

    ResponderEliminar