soy Kseso y esto EsCSS

La cuestión de los menús desplegables o drop down y cómo no liarse

No hay nada más sencillo de construir que un menú desplegable. Y más sencillo aún hacerlo plénamente funcional sólo con Css. Y de cómo lograrlo va este artículo. Corregido y ampliado Abril de 2016.

La cuestión de los menús desplegables o drop down y cómo no liarse

·Por Kseso ✎ 44
La cuestión de los menús desplegables o drop down y cómo no liarse

No hay nada más sencillo de construir que un menú desplegable. Y más sencillo aún hacerlo plénamente funcional sólo con Css. Y dicho lo anterior me adelanto y respondo la pregunta que ya estás lanzando: por recuerdos atávicos y experiencias arcaicas.

Para realizarlos sólo se necesita un grupo de elementos ocultos que se mostrarán tras realizar alguna acción sobre otro. Un evento :hover, por ejemplo.

El típico marcado Html

Antes de continuar, y por si acaso algún visitante lo necesita para comprender mejor lo que sigue, aquí el típico marcado de un menú desplegable construido con listas:

<ul> <li> <a href="#">Opción 1</a><!--Visible--> <ul><!--Lista de segundo nivel: opciones desplegables--> <li><a href="destino1">Opción 1.1</a></li> <li><a href="destino2">Opción 1.2</a></li> </ul> <li> <li><a href="#">Opción 2</a></li> </ul>

Un poco de historia

Hubo un tiempo que era obligado pensar en los IE´s. Especialmente en el difunto IE6. Fuente de todo tipo de dolores de cabeza. Una de sus muchas "bondades" para con los buenos artesanos del código y motivo de inflar el trabajo y las facturas era su "visión particular de la web".

Dos de estas eran su negativa a aplicar las pseudoclases (el :hover por ejemplo) a cualquier otro elemento que no fuese un enlace y que tampoco entendía los selectores de hermano.

Así que era obligado recurrir a otros lenguajes (js, flash...) o a verdaderas obras de ingeniería html para lograrlo sólo con Css. Obra que requería construir tablas falsas y css sólo visibles para IE6 mediante comentarios condicionales.

Compara el código anterior con el necesario para lograr los drop down menús con sólo Css y códigos que pasaban el validador:

<ul> <li class="nivel1"><a href="#" class="nivel1">Opción 1</a> <!--[if lte IE 6]> <a href="#" class="nivel1ie">Opción 1 <table><tr><td> <![endif]--> <ul> <li><a href="#">Opción 1.1</a></li> <li><a href="#">Opción 1.2</a></li> </ul> <!--[if lte IE 6]> </td></tr></table></a> <![endif]--> </li> </ul>

Una explicación detallada y multitud de estos menús son los realizados por Mikel Morote en su página araudi.net. Amén de otros muchos recursos a cada cual más ingenioso siempre pensando en su funcionamiento en IE6 y con códigos Html y Css válidos.

Pero todo este sobreesfuerzo y derroche de energías hoy es ya innecesario. Y lo último que yo recomendaría en 2013 es utilizar esta vía. Excepto en casos mínimos y situaciones muy muy específicas.

El menú desplegable paso a paso

Todo lo anterior es ya historia, y en muchos casos historia olvidada. Pero cuyos efectos aún persisten hoy en día en algunos realizadores. Así que aquí mi granito de arena para que logres menús desplegables plénamente funcionales sólo con Css:

Ver demo

Primer paso: el primer nivel de opciones

Comenzamos por crear las opciones de primer nivel del drop down. Esto es, las que siempre estarán visible y que actuarán como interruptor para mostrar las de segundo nivel u ocultas que añadiré en el segundo paso:

<nav id="menu_gral"> <ul> <li><a href="#">Opción 1</a></li> <li><a href="#">Opción 2</a></li> <li><a href="#">Opción 3</a></li> </ul> </nav>

Para el ejemplo opto por incluir un id en la caja padre del menú <nav id="menu_gral"> para discriminar la lista y sus contenidos de otras listas o menús (nav) que pudiera haber en la página.

También doy por asumido que en la página hay algún tipo de reset. Así que saltamos esa parte y declaramos los estilos para estos:

#menu_gral { font-family: verdana, sans sherif; width: 80%; margin: 1.5rem auto; } #menu_gral ul { list-style-type: none; text-align: center; font-size: 0; } #menu_gral > ul li { display: inline-block; width: 25%; position: relative; background: #FF7361; } #menu_gral li a { text-decoration: none; font-size: 1rem; color: #444; display: block; line-height: 2.5rem; } #menu_gral li a:hover, #menu_gral li a:focus { background: #444; color: #fff; }

En #menu_gral centramos el menú y dejamos un poco de espacio a su alrededor con el margen y su anchura. También dejamos declarada la tipografía utilizada.
En #menu_gral ul retiramos la viñeta que por defecto tienen los elementos de una lista, alineamos al centro sus contenidos (y el de sus descendientes) y por último pongo el tamaño de la tipografía a cero para evitar el espacio de separación entre los li (por su display). Ver explicación detallada.
En #menu_gral > ul li Utilizo el selector de hijo directo (>) para evitar que sus propiedades sean heredadas por los li de segundo nivel (las opciones que se desplegarán). Las otras 2 reglas son porque he optado por un menú en horizontal en la demo con la anchura en cada item homogénea e independiente de su contenido. Son cuatro, así que cada uno al 25% (de su padre). Y muy importante: lo posicionamos como relative para que el desplegable se coloque respecto a él.

Si se quiere el menú en vertical, un item encima de otro, bastaría con cambiar su display: block en los li´s y ajustar la anchura al 100%. Declarando, como es lógico, la anchura de #menu_gral a la necesaria.

En #menu_gral li a quitamos el subrayado propio de los enlaces, ajustamos el tamaño de la tipografía al gusto (recuerda que por herencia estaba a 0) y su color. Al hacerlo elemento de bloque (block) ajustamos automáticamente su anchura a toda la de padre y por lo tanto su zona activa. Aquí viene un punto interesante: si te has fijado, hasta ahora no se han marcado alturas ni he mencionado el centrado en la vertical de los items. Estos dos detalles los conseguimos con el valor de line-height.

En #menu_gral li a:hover, #menu_gral li a:focus jugamos con el Css para poner de manifiesto el item que recibe el foco.

Las propiedades y valores de estas dos últimas reglas sí son heredadas por los enlaces del desplegable porque utilizo selectores de "descendientes" (los elementos del selector separados por un espacio).

El resultado de todo lo anterior es el que ves en la imagen:

Resultado del primer paso del menú desplegable Css (drop down menu)
Resultado del primer paso del menú desplegable Css (drop down menu)

Segundo paso y último: las opciones desplegables

Lo primero es incluir las opciones de segundo nivel en el Html, dentro de aquellos li´s se necesiten y fuera de los enlaces que ya existían de antes:

<nav id="menu_gral"> <ul> <li><a href="#">Opción 1</a> <ul><!-- Segundo nivel desplegable --> <li><a href="#destino1-1">Opción 1.1</a></li> <li><a href="#destino1-2">Opción 1.2</a></li> <li><a href="#destino1-3">Opción 1.3</a></li> </ul> </li> <li><a href="#">Opción 2</a></li>

Lo primero que hay que hacer es ocultar estas opciones para que no se muestren hasta no necesitarlas. Por una cuestión de accesibilidad renunciamos al display: none

#menu_gral li ul { position: absolute; width: 0; overflow: hidden; }

position: absolute; evitará movimientos en el resto de elementos que haya después del menú al mostrarse esta lista.

El siguiente paso es como antes, estilizar este bloque de opciones desplegables en su estado de "reposo" y al recibir el foco para mostrarse. Tanto la lista (ul) como sus hijos: los li´s y sus enlaces del segundo nivel.

#menu_gral li li { display: block; width: 100%; } #menu_gral li:hover li a, #menu_gral li:focus li a { display: block; font-family: monospace; font-size: .9rem; line-height: 1.7rem; border-top: 1px solid #e5e5e5; background: #444; } #menu_gral li li a:hover, #menu_gral li li a:focus { background: #8AA9B8; }

El Css anterior básicamente es para pisar alguno de los estilos heredados o para diferenciar estos items de los del primer nivel./p>

Y para terminar sólo resta mostrar cada grupo de estos enlaces del drop down menu al recibir el :hover, ya sea por ratón o teclado:

#menu_gral li:hover ul, #menu_gral li:focus ul { width: 100%; z-index: 5; margin: 0 -4rem -4rem -4rem; padding: 0 4rem 4rem 4rem; background: #bbb; }

Sencillo. Reescribimos su anchura al 100% (la habíamos declarado 0).
El z-index para que se muestre sobre (eje z) el resto de elementos que haya después de esta lista en el html.
Las declaraciones del margin y padding de esta lista son para crear una pequeña zona de seguridad de 4rem en los laterales y parte inferior de la lista. De esta manera aunque salga el cursor de ella no se cierra. El fondo gris del foco ampliado es para hacerla visible en la demo de abajo o en jsfiddle.

Si quieres saber un poco más sobre esa zona gris de la demo o zona de seguridad para que no se cierre el desplegado tienes el artículo dedicado a este detalle en:

Menú con zona activa de seguridad (foco) ampliada

Variaciones

En base a esta forma de construir los menús desplegables o drop down es sencillo adaptarlo a otros casos, como puede ser que las opciones de primer nivel se muestren en vertical limitando la anchura ocupada por ellos.

En vertical a la izquierda o derecha

Sólo es necesario declararle el valor deseado en width a la caja padre y posicionar la lista de segundo nivel convenientemente (left: 100% para que se muestre a la derecha o right: 100% para que lo haga a la izquierda).

Con eso junto a mantener la naturaleza de los li como elementos de bloque y poco más la apariencia y funcionamiento es el siguiente:

See the Pen BygKGL by Kseso (@Kseso) on CodePen.

En vertical hacia abajo

Y si quisieras que la lista de segundo nivel y sus opciones se desplegasen debajo de su ítem desplazando el resto de opciones visibles, sólo es cuestión de no sacarlas del flujo del documento position: relative, con tamaño 0 (cero) mientras estén replegadas y al desplegarlas hacer que recuperen su altura:

See the Pen LEKZjV by Kseso (@Kseso) on CodePen.

En este caso hay que tener cuidado con cierto comportamiento. Pon el puntero sobre la "opción 2" y desplázate hacia abajo. Al salir el puntero del último de sus ítems de segundo nivel (opción 2.2) verás que ocurre al replegarse: el foco queda no sobre "opción 3" si no sobre el 4º.

Horizontal hacia abajo anchura total

Otra típica y muy empleada variación consiste en que las opciones desplegables ocupen toda la anchura del menú, no de cada item.

Para lograrlo sólo es necesario que las opciones de primer nivel esté con position: estatic para que su tamaño y ubicación dejen de ser el referente para su grupo de ítems desplegables (su contenido).

Como en esta demo:

See the Pen mJyVWP by Kseso (@Kseso) on CodePen.

En el artículo "Mega menú Css. Guía de creación" tienes una explicación detallada de su construcción y códigos, así como otras demos un poco más elaboradas.

Ver post "Mega menú Css. Guía de creación"

Y en base a esta misma variación se puede ir un poco más allá sólo con un poco de imaginación para lograr cosas como este "droUpDown Menu animado puro Css".

Más menús CSS a la carta

Si después de todo el artículo aún te has quedado con hambre de más menús en puro CSS tienes suerte. Con el paso del tiempo he ido publicando distintas entradas sobre ellos. Unas más básicas como las que ya he mencionado y otras un poco más elaboradas o vistosas.

Todos los menús CSS del Blog

Alguno en hexágonos, otros en el famoso hamburger menu, navicon o trigram, otros aparecen letra a letra... étc.

Observaciones finales

  • En los códigos Css de este artículo he priorizado la comprensión de los selectores (hacer más evidente a qué elemento apunta) que el optimizarlos.
  • Lo mismo con las declaraciones. Posíblemente puedieran purgarse algo.
  • La estética está descuidada y la elección de colores y otros detalles es notoriamente mejorable.
  • La ausencia total de transiciones y/o transformaciones css es voluntaria. Bastaría aplicar una pizca de éstas para que su funcionamiento nada tenga que envidiar a los efectos logrados con js. Algunos de ellos los encontrará en otras demos del blog.

Artículo publicado originalmente en Abril de 2013. Actualizado y ampliado en Abril de 2016.

avatar del Editor del blog

the obCSServer ᛯ Ramajero Argonauta, Enredique Amanuense de CSS.
#impoCSSible inside
Dicen que, en español, EsCss es el mejor blog de CSS. Posíblemente exageren.
@Kseso EsCss Kseso