toggle

3 formas de crear un menu responsive

min Meta Descripción
Demostración

Menú adaptable

Se que a mas de uno le habrá ocasionado un dolor de cabeza adaptar el menú, o lograr lo que generalmente se conoce como menu responsive.


La solución sencilla esta en utilizar directamente un menú desplegables (puedes tomar como ejemplo el menú de zkreations), de esta forma, los cambios que necesitas realizar son pocos o directamente nulos en los media query. Si prefieres un menú horizontal que cambie a uno desplegable entonces te invito a seguir leyendo.


Cuerpo HTML

Para empezar, nuestro menú necesita 3 cosas: Un contenedor, un botón que servirá para controlar el despliegue y el menú en sí, el cuerpo html nos quedaría de esta forma:


<div class="menu">
   <span class="drop">
      <svg width="36px" height="36px" viewBox="0 0 48 48"><path d="M6 36h36v-4H6v4zm0-10h36v-4H6v4zm0-14v4h36v-4H6z"></path></svg>
   </span>
   <nav>
      <a href="#">Elemento A</a>
      <a href="#">Elemento B</a>
      <a href="#">Elemento C</a>
      <a href="#">Elemento D</a>
   </nav>  
</div>

En el código anterior, he creado un contenedor con class="menu", es importante ya que nos permitirá controlar el menu nav. Dentro he agregado una etiqueta <span>, que a su vez contiene un vector y servirá para desplegar el menú, y por último una etiqueta <nav> que contiene una lista de enlaces.


Agregando estilos CSS

No nos vamos a quebrar la cabeza pensando en el menú más bonito del universo, así que le daremos un diseño muy sencillo.


.menu {
    position: relative;
}
.menu nav {
    display: block;
    background: rgba(0,0,0,0.5);
    overflow: hidden;
}
.menu nav > a {
    float: left;
    color: #fff;
    padding: 2em;
    border-right: 1px solid rgba(0, 0, 0, 0.35);
    transition: background .2s;
    display: block;
}
.menu nav > a:hover {
    background: rgba(0,0,0,0.4);
}

Ahora necesitamos un estilo para el botón y además debe permanecer oculto mientras nuestro menú se mantenga en horizontal, para ello, agregaremos la propiedad display: none; junto con el diseño.


/*botón*/
.menu .drop {
    color: #fff;
    padding: 2em;
    background: rgba(0,0,0,0.5);
    cursor: pointer;
    display: none; /*ocultamos el botón*/
}
.menu .drop svg {
    fill: #fff;
    vertical-align: middle;
}

Hasta ahora tenemos un menú común y corriente con un botón fantasma, es ahora donde comienza lo bueno. Vamos a crear los estilos que se ejecutan cuando la resolución es muy baja, esto se logra mediante media query de css.


@media only screen and (max-width: 768px) {
    .menu {background: rgba(0,0,0,0.3);}
    .menu .drop {display: inline-block;}/*volvemos a mostrar el botón*/
    .menu nav {/*ahora nuestro menú es una lista flotante*/
        position: absolute;
        z-index: 600;
        background: #121212;
        min-width: 20rem;
        display: none;
    }
    .menu nav > a {/*y los ítems se alinean verticalmente*/
        float: none;
        border: 0 none;
    }
}

Bueno ya tenemos el menú, pero nuestro botón no tiene ninguna función, ya que solo es puro html. Tenemos 3 formas de hacerlo, que nos darán exactamente el mismo resultado, pero que variará en cuanto rendimiento y compatibilidad, esta en cada uno usar el metodo que mas le convenza.


Método 1 - JQuery

Primero necesitamos instalar la ultima version en nuestra página agregando arriba de </head> o de </body> el siguiente código.
Nota: Si ya tienes instalado jquery no es necesario este paso.


<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>

Bien, ahora solo nos queda agregar la función de nuestro querido botón.


<script>
$(document).ready(function(){
    $(".menu > .drop").click(function(){
        $(".menu > .drop").toggleClass("open");
        $(".menu > nav").toggleClass("open");
    });
});
</script>

Lo que hace el codigo anterior es agregar la class open al botón y al menú con la etiqueta <nav> cada vez que se realiza un clic al botón, esto se logra mediante la función toggleClass de JQuery.


Solo nos queda agregar los estilos que afectarán al menú y al botón al momento de obtener la class open.


/*if => open*/
.menu nav.open {
    display: block;/*el menu aparece*/
}
.menu .drop.open {
    background: rgba(0,0,0,0.65);/*el boton cambia de color*/
}

Listo, el menú ya funciona, pero para ello fue necesario cargar un framework, y solo para crear un simple menú.


Método 2 - Vanilla JS

Ahora vamos a realizar exactamente lo mismo, pero con javascript puro, en este caso tenemos que lorgrar el mismo efecto que la funcion toggleClass de JQuery. Para ello agregamos el siguiente codigo arriba de </body>


<script>//<![CDATA[
Array.prototype.forEach = function(b, c) {//reemplazo metodo forEach para mejor compatibilidad
    var a = 0;
    for (ii = this.length; a < ii; a++) a in this && b.call(c, this[a], a, this)
};
Array.prototype.slice.call(document.querySelectorAll(".menu")).forEach(function(b) {//seleccionamos todos los elementos con class "menu"
    var c = b.querySelector(".menu > .drop"),//variable para el botón
        a = b.querySelector(".menu > nav");//variable para el menú
    c.onclick = function(b) {//toggleClass
        a.className && /(^|\s)open(\s|$)/.test(a.className) ? (a.classList.remove("open"), c.classList.remove("open"), b.preventDefault()) : (a.classList.add("open"), c.classList.add("open"), b.preventDefault())
    }
});
//]]></script>

En este caso hacemos uso de forEach, pero para mejorar la compatiblidad lo reemplazamos, Esta modificacion la realizo mi amigo Jose Gregorio al código que inicialmente habia escrito. Luego seleccionamos el contenedor del menú por medio de su class y definimos una variable para seleccionar el menú nav y otra para el botón. Por último, mediante una función se agrega la class open con un clic, y en caso de que ésta ya se encuentre la removemos.


Nuevamente tenemos la class open, así que solo nos queda agregar el css.


/*if => open*/
.menu nav.open {
    display: block;/*el menu aparece*/
}
.menu .drop.open {
    background: rgba(0,0,0,0.65);/*el boton cambia de color*/
}

Este método tiene un rendimiento muy superior aunque a simple vista no sea perceptible.


Método 3 - Pure CSS

Hasta este punto ya entendemos que solo necesitamos comprobar si el menú se encuentra abierto o cerrado. Aunque Vanilla JS nos parezca la mejor solución y la mas rápida, tambien podemos lograr lo mismo solo con css y como no requiere código javascript de ningún tipo, obtendremos un mejor rendimiento. Para empezar vamos a tener que cambiar un poco el cuerpo html:


<div class="menu">
   <input id="toggle" type="checkbox"/>
   <label for="toggle" class="drop">
      <svg width="36px" height="36px" viewBox="0 0 48 48"><path d="M6 36h36v-4H6v4zm0-10h36v-4H6v4zm0-14v4h36v-4H6z"></path></svg>
   </label >
   <nav>
      <a href="#">Elemento A</a>
      <a href="#">Elemento B</a>
      <a href="#">Elemento C</a>
      <a href="#">Elemento D</a>
   </nav>  
</div>

Lo que hemos hecho es agregar un input, del tipo checkbox, y para darle estilos reemplazamos el span por una etiqueta label que tambien controla el estado del input (si se encuentra seleccionado o no). Este pequeño cambio, es el reemplazo de la función toggleClass pero a puro html. Ahora solo nos queda agregar los estilos.


/*pure css*/
.menu input {display: none}/*ocultamos el input*/
#toggle:checked ~ nav{
    display: block;
}
#toggle:checked ~ label{
    background: rgba(0,0,0,0.65);
}

Lo que hacemos es ocultar el input ya que solo lo necesitamos para comprobar un estado del menú. Cuando nuestro botón label es pulsado, marcará el input y esto causará que su estado cambie a checked, y si se vuelve a pulsar se des-marcará perdiendo el estado checked, por lo tanto en el css agregamos los estilos respectivos dejando el menú funcional.

Para ver un ejemplo de los 3 métodos puedes consultar la página de ejemplo.


¿Cual es el mejor método?

Si tienes una web basada en JQuery entonces el mejor método sería usarlo tambien en el menú. Si no utilizas JQuery entonces no es nada práctico ni recomendable incluirla solo por un menú, es mejor optar por Vanilla JS, logrando así mejor rendimiento. Y si quieres aún mas velocidad porque no te preocupa la compatibilidad con navegadores viejos (IE8 o inferior, firefox 3 o inferior, etc) entonces el método mediante css es el indicado para ti.