Cómo Crear un Menú Responsive

CSShtmljQuery

En este tutorial vamos a ver cómo crear una barra de menús responsiva, responsive o como queráis llamarle. Gracias a este tipo de menú, conseguiremos que se vea bien tanto en ordenadores de sobremesa como en dispositivos móviles. Para ello podríamos utilizar algún framework CSS como Bootstrap, Bulma o Foundation, aunque en este caso no tendría demasiado sentido un tutorial, ya que ambos frameworks incluyen este componente de serie.

Lo que haremos será crear el menú desde cero utilizando HTML, CSS y JavaScript. Además, aunque utilices frameworks en tu día a día, siempre que es una buena idea que, como desarrollador, crees ciertos componentes por ti mismo aunque solo sea una vez, ya que de este modo podrás comprender mejor su funcionamiento.  Eso sí, para facilitar algo ciertas tareas utilizaremos jQuery.

Creación básica del menú

Primero crearemos el menú de modo que funcione en pantalla comunes y, seguidamente, veremos cómo adaptar el menú a móviles.

Código HTML

Lo primero que vamos a hacer es agregar el código HTML que vas a necesitar, que por ahora todavía no contendrá estilos asociados. Primero utilizamos la etiqueta semántica nav, que se utiliza cuando queremos definir un menú de navegación en su interior.

Lo que hacemos en el código que ves a continuación es crear los elementos del menú como una lista no ordenada ul, cuya clase es nav-menu. En su interior definimos los enlaces mediante etiquetas li que contienen una etiqueta a, que representa a cada enlace. También es posible que a cada enlace le siga otro menú a modo de submenú desplegable, que representaremos mediante una etiqueta ul, con la clase nav-submenu.

<nav>
  <div class="nav-movil">
    <a id="nav-boton" href="#!">
      <span></span>
    </a>
  </div>
  <ul class="nav-menu">
    <li><a href="/inicio">Inicio</a></li>
    <li><a href="/informacion">Información</a></li>
    <li>
      <a href="/servicios">Servicios</a>
      <ul class="nav-submenu">
        <li><a href="/programacion">Programación</a></li>
        <li><a href="/diseno">Diseño</a></li>
        <li><a href="/hosting">Hosting</a></li>
      </ul>
    </li>
    <li><a href="/contacto">Contacto</a></li>
  </ul>
</nav>

Si estás siguiendo este ejemplo, verás que por ahora solamente se muestra una lista de elementos sin estilos. Aquí tienes una captura de pantalla:

A continuación veremos cómo agregar los estilos necesarios para obtener un menú en condiciones. Decir también que, como ves, no se muestra el div cuya clase es nav-movil, y asís era hasta que lleguemos a la parte en la que agregamos el menú pensado para pantallas más pequeñas, como la de un dispositivo móvil.

Código CSS

Habitualmente usaríamos algún preprocesador de CSS como SASS o LESS debido a sus grandes ventajas. Sin embargo, en este ejemplo nos limitaremos a utilizar CSS puro. Vamos a ver el código CSS por partes.

nav {
  float: left;
  background: #001628;
  width:100%;
}

nav ul {
  list-style: none;
  margin: 0;
  padding: 0;
  background:#001628;
}

Lo primero que hemos hecho ha sido asignar un ancho del 100% a la etiqueta nav para que así ocupe todo el ancho de la pantalla. También hemos definido ciertos estilos para el menú de nivel superior definido mediante la etiqueta ul, eliminando el margen y el padding predeterminados y los puntos o bullet points de la lista. También le hemos dado a un color de fondo.

Ahora vamos a definir los estilos de cada botón del menú:

nav ul li {
  float: left;
  position: relative;
}

nav ul li a {
  display: block;
  padding: 0 20px;
  line-height: 70px;
  color: #72e0d1;
  text-decoration: none;
}

nav ul li a:hover {
  background: #2581dc;
  color: #ffffff;
}

Lo que hemos hecho primero es alinear los elementos li de la lista del menú para que se muestren alineados a la izquierda, uno después del otro. De este modo ya no se mostrarán en vertical. Luego hemos definido los estilos de los enlaces, eliminado los estilos asignados por el navegador por defecto y agregando la propiedad display: block; para que se muestren en vertical, uno después del otro. También hemos agregado 20px de padding por pura estética.

Seguidamente, definimos los estilos de los enlaces o botones para el momento en el que el cursor se posa encima de cada uno de ellos. Para ello usamos el selector :hover. Básicamente, cambiamos el color de fondo del botón y el color del texto.

Ahora vamos a definir los estilos de los submenús:

nav ul li a:not(:only-child):after {
  padding-left: 5px;
  content: ' ▾';
}

.nav-submenu {
  width:auto;
  position: absolute;
  z-index: 1;
  box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.3);
  width: auto;
}

.nav-submenu li {
   width: 100%;
   white-space: nowrap;
}

Lo primero que hemos hecho es agregar una flecha apuntando hacia abajo después de cada elemento primario del menú que contenga un submenú. Para ello hemos seleccionado los enlaces que no tienen otros elementos al mismo nivel, como hermanos, mediante el selector :not(:only-child). Para agregar una flecha tras estos elementos, usamos el selector :after y el atributo content.

Luego hemos agregado una sombra al submenú y lo hemos posicionado de absoluta para así salir del flujo relativo del documento, pudiendo posicionar el menú correctamente. Cuando asignamos a un elemento la propiedad y valor posicion: absolute el elemento se posicionará de forma relativa al primer elemento anterior que esté posicionado relativamente mediante la propiedad position: relative. Además, hemos asignado a los submenús un valor z-index: 1 para que siempre sean estén por encima del menú de nivel superior, garanzando su visibilidad.

Por último hemos hecho que los elementos de los submenús ocupen el 100% del ancho del menú gracias a las propiedades width: 100% y white-space: nowrap, forzando mediante la propiedad white-space que el texto continúe en la misma línea.

Una vez aplicado el código CSS, el resultado será el siguiente.

Eso sí, todavía nos falta un detalle. Por ahora los menús de nivel secundario son visibles. Sin embargo, lo ideal sería que fuesen visibles para poder mostrarlos usando JavaScript cuando hagamos clic en el enlace de nivel superior en el que están contenidos. Para ello, debemos asignar la propiedad display: none a los submenus. Es decir, que el código CSS de la clase .nav-submenu deberían quedar finalmente así:

.nav-submenu {
  width:auto;
  position: absolute;
  z-index: 1;
  box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.3);
  width: auto;
  display: none;
}

A continuación vamos a ver el código JavaScript que utilizaremos.

Código JavaScript

En este apartado agregaremos el código JavaScript necesario, para lo cual utilizaremos la librería jQuery. Lo que vamos a hacer es crear una función que se ejecute cuando el DOM del navegador esté listo; es decir, cuando el código HTML se haya cargado. En su interior agregaremos luego varias acciones.

$(document).ready(function() {
  // Aquí agregaremos algunos eventos
});

Lo primero que hacemos se crear una acción en el interior de la función anterior que se ejecute cuando hagamos clic en en aquellos elementos del menú que tienen descendientes; es decir, que contienen un submenú además de un enlace. Para seleccionar estos elemento usamos el selector nav ul li > a:not(:only-child) y, seguidamente, aplicamos un evento click junto con el código de la función que queremos ejecutar:

$('nav ul li > a:not(:only-child)').click(function(e) {
  // Aquí agregamos las acciones del evento
});

En caso de que no lo sepas, la variable e se refiere al evento del propio clic que hace el usuario. Esto es lo que nos permite filtrar o identificar al elemento sobre el que se hace clic.

Mediante la función $(this).siblings('.nav-submenu').toggle() mostramos el submenú del elemento en el que hemos hecho clic, puesto que la función toggle se utiliza para modificar la propiedad display, haciéndolos visibles en caso de que su valor sea display: none y viceversa. $(this) especifica que solamente nos estamos refieriendo al elemento sobre el que se ha ejecutado la acción, que en este caso es un clic. Esta acción estará dentro del evento click que hemos creado:

$(this).siblings('.nav-submenu').toggle();

Además, también en el interior del evento anterior, ocultamos aquellos otros posibles submenús de otros elementos que estuviesen visibles mediante la función  $(‘.nav-submenu’).not($(this).siblings()).hide(), que excluye al elemento actual gracias a la función .not($(this).siblings()):

$('.nav-submenu').not($(this).siblings()).hide();

Finalmente, agregamos la función e.stopPropagation()al evento actual para que otras funciones referidas al clic del usuario no se ejecuten:

e.stopPropagation();

Todos los elementos anteriores se incluían en el interior del evento referido al clic del usuario. Sin embargo, ahora agregaremos otro evento referido a un clic genérico, en cualquier parte del documento, que ocultará todos los menús cuando hagamos clic. Dado que hemos agregado la función e.stopPropagation() en la acción anterior, los elementos del menú en los que el usuario haga clic no se verán afectados, pero sí el resto:

$('html').click(function() {
  $('.nav-submenu').hide();
});

Y ya no necesitamos agregar nada más. Aquí tienes el código jQuery completo:

$( document ).ready(function() {

  $('nav ul li > a:not(:only-child)').click(function(e) {
    $(this).siblings('.nav-submenu').toggle();
    $('.nav-submenu').not($(this).siblings()).hide();
    e.stopPropagation();
  });

  $('html').click(function() {
    $('.nav-submenu').hide();
  });
});

Creación del menú responsivo

Hemos llegado a la parte en la que agregamos el menú móvil. Es decir, haremos que cuando el menú cambie de aspecto, cuando se vea en pantallas de reducido tamaño, mostrándose el típico botón con tres líneas.

Código CSS

Al principio no habíamos agregado ningún estilo a la clase nav-movil. Es hora de hacerlo:

.nav-movil {
  display: none;
  position: absolute;
  top: 0;
  right: 0;
  background: #001628;
  height: 70px;
  width: 100%;
}

Tal y como puedes ver hemos agregado la propiedad display: none, que ocultará el menú por defecto, ya que solamente queremos que este elemento se muestre cuando la pantalla del dispositivo o la ventana del navegador es de un tamaño reducido.

A continuación vamos a agregar una media query CSS que entrará en juego cuando el tamaño de la pantalla o de la ventana sea inferior a 980px:

@media screen and (max-width: 979px) {
  .nav-movil {
    display: block;
  }

  .nav-menu {
    display: none;
  }
}

Lo que hemos hecho es mostrar el menú móvil, definido mediante la clase .nav-movil cuando la pantalla tenga un ancho inferior a 980px. Además, en dicho caso también ocultamos el menú que se muestra en pantalla anchas, identificado mediante la clase .nav-menu.

Ahora vamos a mostrar un sencillo botón que nos permita abrir el menú desplegable en su versión móvil. Primero crearemos un botón muy sencillo que solamente constará de un fondo blanco y, al final del tutorial, veremos cómo darle la típica forma de hamburguesa:

#nav-boton {
  position: absolute;
  left: 20px;
  top: 25px;
  cursor: pointer;
  width:20px;
  height: 10px;
  padding: 5px 15px 15px 0px;
  background: #fff;
  z-index:1;
}

En el siguiente apartado agregaremos un acción a este botón que cambie el valor de la propiedad display a su valor block o none cuando hagamos clic en este botón. Sin embargo, no son pocos los desarrolladores que se olvidan del caso en el que el menú pueda haberse ocultado en su versión móvil mediante este botón. ¿Qué ocurre si el usuario decide cambiar el tamaño de la ventana del navegador? Pues que el menú no se verá, por lo que debemos forzar que siempre sea visible en su versión de escritorio mediante la siguiente media query:

@media screen and (min-width: 980px) {
  .nav-menu {
    display: block !important;
  }
}

El flag important, dará prioridad a este valor sobre cualquier otro valor establecido manualmente.

Seguidamente, vamos a aplicar ciertos estilos a la versión móvil del menú, por lo que debemos agregar los siguientes estilos dentro de la media query que solamente se aplica cuando el tamaño de la pantalla es inferior a 980px:

@media screen and (max-width: 979px) {
  .nav-movil {
    display: block;
  }

  .nav-menu {
    display: none;
    margin-top: 70px;
  }
  
  .nav-menu li {
    float: none;
  }
  .nav-menu li a {
    padding: 20px;
    line-height: 20px;
  }
  .nav-menu li ul li a {
    padding-left: 30px;
  }
  
  .nav-submenu {
    position: static;
  }
}

Como ves, asignamos el valor de static a la posición de los elementos con la clase .nav-submenu para evitar que se superponga sobre otros elementos del menú. Si quieres, prueba a cambiar su valor para que veas de una forma más clara a qué me refiero.

También hemos eliminar el valor float: left de los elementos del menú para que se muestran los elementos del menú en vertical y no en horizontal. Por último, también hemos realizado pequeños ajustes a los márgenes del menú para que tenga un mejor aspecto en su versión móvil.

Por ahora todavía no verás nada, pero no te preocupes, ya que en el siguiente apartado veremos cómo mostrar el menú mediante JavaScript.

Código JavaScript

Ahora vamos a agregar el código jQuery necesario para que se muestren o se oculten los elemento del menú cuando hagamos clic en el botón del menú móvil. Este código lo debes agregar en el interior de la función $(document).ready que hemos definido antes:

$('#nav-boton').click(function() {
  $('nav ul').toggle()
})

Ahora, los elementos que estaban ocultos por defecto en versión móvil se mostrarán o se ocultarán mediante este botón.

Con este ya tenemos un menú perfectamente funcional, pero todavía tenemos que mejorar su aspecto.

Mejora del botón móvil

Para agregar un botón con forma de hamburguesa nos basaremos en el código de otro tutorial en el que explico cómo crear un botón de manú animado, en el que nos valemos exclusivamente de CSS.

El botón contiene una etiqueta span en su interior y, lo que haremos, será representarlo como una línea. Además, usaremos los selectores before y after para insertar una línea superior y otra inferior, dando la sensación de que existen tres líneas:

#nav-boton span,
#nav-boton span:after,
#nav-boton span:before {
  transition: all 500ms ease-in-out;
  border-radius: 1px;
  height: 5px;
  width: 40px;
  position: absolute;
  display: block;
  content: "";
  background: #fff;
}

Como ves, también hemos agregado la duración de la transición para el efecto que aplicaremos cuando se haga clic en el botón. Debemos darle cierta separación a las líneas superior e inferior de modo que no estén juntas:

#nav-boton span:before {
  top: -10px;
}
#nav-boton span:after {
  bottom: -10px;
}

Mediante JavaScript agregaremos la clase activo o inactivo al botón para que cambie de estado. Lo que queremos es que se muestres tres líneas horizontales cuando el botón está inactivo, y una «X» cuando el botón se haga clic en él. Para ello, haremos la línea del medio transparente cuando se active el botón:

#nav-boton.activo span {
  background-color: transparent;
}

Por último agregaremos el efecto de rotación.

#nav-boton.activo span:before,
#nav-boton.activo span:after {
  top: 0;
}

#nav-boton.activo span:after {
  transform: rotate(-45deg);
}

#nav-boton.activo span:before {
  transform: rotate(45deg);
}

Finalmente modificamos también el evento en el que definimos las acciones de clic en dicho botón, Agregamos una función que agregue o elimine la clase activo del propio botón. la función quedaría finalmente así:

$('#nav-boton').click(function() {
  $('nav ul').toggle()
  $('#nav-boton').toggleClass("activo");
})

Aquí tienes finalmente el resultado del botón cuando haces clic sobre él:

Y esto es todo lo que necesitas incluir en una barra de navegación básica.

Resultado: Código completo

Puedes encontrar el código y resultado final en este pen. También puedes encontrarlo a continuación:

See the Pen Responsive menu by Eduardo (@edulazaro) on CodePen.36754

En el ejemplo de CodePen hemos agregado la barra de navegación en el interior de una etiqueta section, es que es una etiqueta contenedora que se usar para definir las diferente secciones de un documento HTML.

<section class="navigation">
  <!-- Aquí irá el código del menú -->
</section>

La sección navigation podría usarse también para definir el fondo y el la altura de la barra de menús:

.navigation {
  height: 70pxt;
  background: #001628;
}

También puedes agregar más cosas, como por ejemplo un logotipo, tal y como puedes ver en el ejemplo completo, en GitHub. Si quieres puedes clonar el repositorio y realizar todos los cambios que quieras sobre este ejemplo.


Avatar de Edu Lazaro

Edu Lázaro: Ingeniero técnico en informática, actualmente trabajo como desarrollador web y programador de videojuegos.

👋 Hola! Soy Edu, me encanta crear cosas y he redactado esta guía. Si te ha resultado útil, el mayor favor que me podrías hacer es el de compatirla en Twitter 😊

Si quieres conocer mis proyectos, sígueme en Twitter.

3 comentarios en “Cómo Crear un Menú Responsive

  1. Hola. Gracias por este ejemplo. Te hago una pregunta. Si quiero que el submenú se despliegue sólo con un hover en vez de con el click, cómo hago para que no se cierre automáticamente al bajar hacia el submenú? Es decir que se mantenga abierto mientras esté sobre el submenú y se cierre al salir del mismo. Gracias

Deja una respuesta

“- Hey, Doc. No tenemos suficiente carretera para ir a 140/h km. - ¿Carretera? A donde vamos, no necesitaremos carreteras.”