Menú de navegaciónMenú
Categorías

La mejor forma de Aprender Programación online y en español www.campusmvp.es

?id=25bd39bc-7429-4b53-b794-5b257cffc96d

Cómo hacer un menú vertical desplegable con HTML y CSS (con y sin JavaScript)

Menu vertical desplecagble con HTML5 y CSS3 (con y sin JavaScript)

En este pequeño tutorial vamos a ver cómo crear el típico menú lateral vertical desplegable. Seguro que lo has visto en mil sitios porque es muy útil, sobre todo cuando necesitas maquetar una web responsive que tiene un menú de primer nivel demasiado grande.

En principio, este menú permanece oculto y, al mostrarse, empuja al div del contenido principal. Primero lo veremos usando JavaScript y luego solo con HTML y CSS. Que conste que hay muchas formas de hacerlo, estas dos solo son un par de ellas.

En este sencillo ejemplo (que puedes ver en directo aquí) tenemos simplemente dos divs. El que envuelve al menu (id="sidebar") y el del contenido principal.

Cuando el menú está abierto, el sidebar tiene un ancho fijo y está posicionado como fixed, por si hay que hacer scroll en el contenido. Así que, al div del contenido le damos un margin-left igual al ancho del sidebar.

La clave está en que, al hacer clic en "Abrir menu", hacemos 3 cosas a través de JavaScript:

  • Al div lateral le asignamos un ancho fijo
  • Al div del contenido le asignamos un margen por la izquierda igual al ancho de la columna lateral
  • Ocultamos el link de "Abrir menú" y mostramos el de "Cerrar menú"

Las animaciones son CSS3, no hay JavaScript involucrado.

Y al hacer clic en cerrar, simplemente recorremos el camino inverso.

Este sería el html de los enlaces para abrir y cerrar:


    <a id="abrir" class="abrir-cerrar" href="javascript:void(0)" onclick="mostrar()">
        Abrir menu
    </a>
    <a id="cerrar" class="abrir-cerrar" href="javascript:void(0)" onclick="ocultar()">
        Cerrar menu
    </a>
    

Y estas las funciones de JavaScript que llamamos:

 
<script>
function mostrar() {
    document.getElementById("sidebar").style.width = "300px";
    document.getElementById("contenido").style.marginLeft = "300px";
    document.getElementById("abrir").style.display = "none";
    document.getElementById("cerrar").style.display = "inline";
}

function ocultar() {
    document.getElementById("sidebar").style.width = "0";
    document.getElementById("contenido").style.marginLeft = "0";
    document.getElementById("abrir").style.display = "inline";
    document.getElementById("cerrar").style.display = "none";
}
</script>

¿Y si pudiéramos hacer un menú muy parecido pero sin JavaScript?

Pues sí, se puede. El menú sin JavaScript se basa en lo mismo en lo que se refiere a las propiedades de los divs del menú y de los contenidos. Lo único que varía es la forma de controlar este comportamiento.

Descargo de responsabilidad y aviso a navegantes:

Lo que vamos a hacer con HTML y CSS en este ejemplo es una chapuza de proporciones bíblicas que se salta las buenas prácticas del marcado de HTML semántico y del desarrollo web front end en general. Es interesante hacerlo como ejercicio experimental para ser conscientes de que, conociendo bien CSS y echándole imaginación, se pueden hacer cosas muy interesantes, pero la capa de comportamiento se debería manejar siempre a través de JavaScript.

Bien, una vez dicho esto, ¿cómo controlaremos si el menú está abierto o cerrado? Pues usando un input del tipo checkboxa través de la pseudoclase :checked, que comprueba si el campo está marcado o no. En la práctica es como si almacenásemos un valor booleano en una variable de JavaScript, pero en CSS.

    <input id="abrir-cerrar" name="abrir-cerrar" type="checkbox" value="" />
    <label for="abrir-cerrar">
        &#9776; <span class="abrir">Abrir</span><span class="cerrar">Cerrar</span>
    </label>

El input lo ocultaremos con CSS, porque es suficiente con tener visible el placeholder para hacer clic sobre él:

    input#abrir-cerrar { visibility:hidden; position: absolute; top: -9999px; }

Podríamos ocultarlo también con display:none; pero recuerdo haber leído en algún lado (lo siento, no recuerdo dónde) que puede dar problemas en algunos móviles. Siendo sincero, nunca me he encontrado este caso pero más vale prevenir, así que lo he ocultado con visibility:hidden; y luego lo posiciono en absoluto para llevármelo bien lejos, así no me molesta ocupando sitio al renderizar en el navegador.

Dentro del placeholder he colocado dos <span> que me servirán para mostrar u ocultar la palabra "abrir" o "cerrar" a conveniencia.

Ahora, simplemente nos queda escribir los estilos en los que se mostrará el menú cuando el input esté activo:

    
    input#abrir-cerrar:checked ~ #sidebar {
        width:300px;
    }
    input#abrir-cerrar:checked + label[for="abrir-cerrar"], input#abrir-cerrar:checked ~ #contenido {
        margin-left:300px;
        transition: margin-left .4s;
    }
    input#abrir-cerrar:checked + label[for="abrir-cerrar"] .cerrar { 
        display:inline;
    }
    input#abrir-cerrar:checked + label[for="abrir-cerrar"] .abrir {
        display:none;
    }
    

Si te estás preguntando qué hace el símbolo "~", pues bien, es un selector de CSS parecido a "+" pero con una sutil diferencia:

  • El selector "+" selecciona el elemento que esté inmediatamente a continuación en el marcado html
  • En cambio, el selector "~" selecciona el elemento que esté a continuación pero no es necesario que esté inmediatamente a continuación

Y esto es todo, ya tendríamos nuestro menú funcionando que puedes ver aquí. Por cierto, puedes descargarte los dos ejemplos juntos haciendo clic aquí (ZIP 6,7 KB).

¿A que es sencillo? Ahora ya sólo faltaría poner el resto del diseño de nuestra página a nuestro gusto, y quizá hacer algún ajuste menor del tamaño del menú para alguna resolución de móviles.

Pero eso ya te toca a ti. Pruébalo y me cuentas en los comentarios.

Fecha de publicación:
Pablo Iglesias Pablo Iglesias es diseñador Web. Cuenta la leyenda que, anidando listas HTML en busca de la página semánticamente perfecta, provocó un agujero espacio-temporal en IE tan grande, que en Redmond tuvieron que llamar al comando SG1 de Stargate para evitar una invasión alienígena. Puedes seguirlo en twitter ( @piglesias). Ver todos los posts de Pablo Iglesias
Archivado en: Desarrollo Web | Trucos

Boletín campusMVP.es

Solo cosas útiles. Una vez al mes.

🚀 Únete a miles de desarrolladores

DATE DE ALTA

x No me interesa | x Ya soy suscriptor

La mejor formación online para desarrolladores como tú

Comentarios (35) -

hey amigo, si bien el codigo no es el mejor me ayudaste a entender como hacer esa transición, gracias, en verdad te lo agradezco

Responder

Pablo Iglesias
Pablo Iglesias

Muchas gracias Christian :-)

Responder

Jorge Durán
Jorge Durán

Me ayudo bastante el código. El único problema que tengo, es que cuando hago scroll hacia abajo y uso el menú, la página se sube y no se queda donde se quedó.

Responder

Pablo Iglesias
Pablo Iglesias

Hola Jorge,

Para que no se te mueva  el scroll al principio de la página al cerrar el menú con JavaScript, sustituye la almohadilla "#" por "javascript:void(0)" en el atributo href del enlace de cerrar.

Saludos

Responder

hola, justo es lo que estaba buscando, me sirvió bastante, solo una duda, como hago que aparezca al iniciar la pagina y luego ocultarlo en vez de hacerlo aparecer.

Responder

La solución que encontré es reemplazar el body escribir por <body onload="mostrar()">

Responder

Fabian Lopez
Fabian Lopez

No me funciona con onload en el body, como puedo hacer para mostrarlo al cargar la web

Responder

Pablo Iglesias
Pablo Iglesias

Hola Fabián, mejor usa la versión sin JavaScript, que te resultará más sencillo. Te he respondido en el otro comentario, más abajo.

Simplemente añádele al input el atributo "checked" y ya te aparecerá abierto por defecto.

O sea, esto:

<input type="checkbox" id="abrir-cerrar" name="abrir-cerrar" value="" checked>

Saludos

Responder

Excelente explicacion, mejor de ahi se daña.

Gracias...

Responder

Hola, estoy intentando tener un menu de cada lado pero no lo logro, como podria hacerlo?

Muchas Gracias, excelente post

Responder

Pablo Iglesias
Pablo Iglesias

Hola Jorge,

Lo más sencilllo es que el segundo menú tenga una clase o una id diferente y a partir de ahí jugar con los márgenes que necesites para cada caso.

Tal y como está definido el div ".sidebar", para colocarlo a la derecha solo tendrías que sustituír left:0 por right:0 en sus reglas CSS. Podrías usar una clase para las reglas comunes de ambos menús y luego definir las específicas a través de una segunda clase o ID según el menú esté a la izquierda o derecha.

El "disparador" que muestre cada menú también tendrá que ser diferente, a no ser que quieras mostrar y ocultarlos a la vez.

Saludos

Responder

Mil gracias por tomarte el tiempo en ayudarme :)
Voy a probar de montarlo

Responder

Hola buen dia quisiera dejar el menu abierto en escritorio como en movil oculto como podria hacerlo ayuda please.

Responder

Mau Flores
Mau Flores

Mil gracias por compartir, cuando tenga la oportunidad ayudaré a alguien mas. GRACIAS, muy util tu codigo.

Responder

hola
Si se usa display:none;  para ocultar el input en el navegador FireFox crea un conflicto  si el elemento contenedor tiene la declaracion  position: relative o osition: absolute

Responder

Pablo Iglesias
Pablo Iglesias

Hola,
No sé qué clase de conflicto te puede suceder, pero supongo que al modificar position la maquetación se te complica porque se altera el flujo normal del documento y se crea un nuevo contexto de posicionamiento.

No te puedo dar una solución universal pero sí que te animo a investigar cómo funciona la propiedad "position", ya que a partir de ahí podrás encontrar la solución adecuada.

Quizá este post relacionado te pueda arrojar algo de luz: www.campusmvp.es/.../...-usar-position-sticky.aspx

Responder

mesa comedor extensible
mesa comedor extensible

Felicidades Pablo, el codigo es el mejor me ayudaste a entender como hacer esa transición, gracias, en verdad te lo agradezco

Responder

muchas gracias por la ayuda. ojala te forres!

Responder

Oye tengo una duda ya hice mi menú pero cuando lo pongo en modo responsive y seleccionó una parte del menú y me lleva a la sección de la página me sigue apareciendo el menú como le puedo hacer para quitarlo y pueda ver bien esa sección

Responder

Pablo Iglesias
Pablo Iglesias

Pues, o bien pones el enlace completo a la página además del ancla a la sección para que se recargue la página completa; o bien usas JavaScript para devolver el menú a su estado inicial tras hacer clic.

Responder

Como hago para copiar el  ejemplo completo,es decir el css y html para una pagina que estoy haciendo.

Responder

José Manuel Alarcón
José Manuel Alarcón

Si lees el artículo completo verás que al final tienes, destacado en negrita, un enlace con todo el código de ejemplo.

Saludos.

Responder

Hola,! Genial la explicación, pero tengo una duda como podría abrir el menú sin que recorra el cuerpo de la pagina, sino que flote.

Responder

Pablo Iglesias
Pablo Iglesias

¡Ay! No tengo claro si entiendo bien la pregunta.

No sé si te refieres a que el menú no tenga el alto del viewport o a que se quede fijo flotando con position:sticky (en este post puedes verlo: www.campusmvp.es/.../...-usar-position-sticky.aspx)   :-S

Responder

Hola!! Muy buena explicacion!!
Pero queria preguntar algo, estoy con un problema. Por algun motivo el boton "cerrar" no aparece. Es decir, donde pone abrir, al clickear se abre la sidebar tal cual tu ejemplo, pero la palabra "cerrar" se queda detras, sin embargo, donde deberia estar realmente "cerrar" funciona como boton.
Cual podria ser el problema? Tengo todo tal cual esta en tu ejemplo, pero no se por que tengo ese error

Responder

Hola Sabrina:

Muchas gracias. Tal y por lo que cuentas, es como si el elemento que contiene "cerrar" tuviese un "position:absolute" o si se hubiese quedado por error fuera de la etiqueta <label>.

Revisa por ahí, a ver si hay suerte y te sirve.

Saludos

Responder

Hola Pablo!! Muchas gracias por tu respuesta.
Lo he mirado tal como dijiste pero no encuentro el problema.
Este es mi label:
<label for="abrir-cerrar">
    &#9776;
    <span class="abrir">Abrir</span>
  
    <span class="cerrar">Cerrar</span>
</label>

y este en el css:

label[for="abrir-cerrar"]{
    cursor: pointer;
    padding: 1rem;
    background-color: #333;
    color: #fff;
    display: inline-block;
    width: 100%;
}
.cerrar {
    display: none;
}
input#abrir-cerrar:checked + label[for="abrir-cerrar"] .cerrar{
    display: inline;  
}

la unica forma en que logro que la palabra "cerrar" se mueva a donde debe ir es si le agrego un margin-left, pero el ☰  no se mueve

Responder

Hola, tengo una duda, quiero que el menú se habrá a la derecha (ya lo cambie), solo que cuando le doy click en abrir menú, todo el contenido se mueve al centro como le cambio la posición del contenido al estar abierto el menú  

Responder

Gran truco, me ayudo demasiado!

Responder

Me sirivio tu ejemplo, no se puede comentarme como hacer que en el menu que se despliega tomo la forma de una columna con iconos, ojala puedas darme alguna diea, estoy practicantdo. gracias

Responder

para meterle mi contenido al menú como seria? soy principiante:(

Responder

Como puedo poner en el lateral izquierdo, texto con una referia que diga codigo

Responder

Fabian Lopez
Fabian Lopez

Hola queria saber que cambio para que el menu aparezca abierto y solo si lo quiere cerrar presiona el boton cerrar

Responder

Pablo Iglesias
Pablo Iglesias

Hola Fabián, simplemente añádele al input el atributo "checked" y ya te aparecerá abierto por defecto.

O sea, esto:

<input type="checkbox" id="abrir-cerrar" name="abrir-cerrar" value="" checked>

Responder

catalogo unique
catalogo unique

Excelente aporte, me ha servido de mucho.

Responder

Agregar comentario

Los datos anteriores se utilizarán exclusivamente para permitirte hacer el comentario y, si lo seleccionas, notificarte de nuevos comentarios en este artículo, pero no se procesarán ni se utilizarán para ningún otro propósito. Lee nuestra política de privacidad.