En este HowTo podrás crear un menú Responsive con desplazamiento vertical de 2 niveles utilizando HTML5, CSS+Sass y JavaScript.
Los pasos que vamos a seguir para realizar el menú son los siguientes:
- Vamos a crear el menú haciendo uso de listas de HTML5 y en caso de necesitar un segundo nivel anidaremos una segunda lista dentro del elemento <li> que hará de padre.
- Para la maquetación vamos a usar el lenguaje Sass para poder controlar el CSS de una manera más eficaz.
- Y por último con JavaScript, añadiremos las interacciones que pueda a realizar el usuario tanto en la versión de escritorio como en la de responsive.
Este será el aspecto del menú una vez desplegado en su versión de escritorio:
Y este será su aspecto una vez desplegado en su versión Responsive.
Una prueba funcional se puede comprobar siguiendo el siguiente enlace
Creando el menú con elementos de lista HTML
La forma más coherente para crear el menú es usar elementos <ul> de HTML5 que nos permiten crear listas. He incluido una clase .container ya que haremos uso de bootstrap para centrar el menú.
1 2 3 4 5 6 7 8 9 |
<header> <nav class="navbar" id="navbar"> <div class="content container"> <!-- aqui el menú --> </div> </nav> </header> |
El siguiente paso es incluir el menú y que vamos a dividir en 2 partes:
- Por un lado tenemos que incluir la barra de navegación Responsive.
- Y por otro lado el menú que contiene todos los elementos.
1 2 3 4 5 6 |
<div class="responsive"> <ul> <li class="icon"><a href="#" class="close">☰</a></li> <li class="home"><a href="https://www.artegrafico.net" title="artegrafico.net">artegrafico.net</a></li> </ul> </div> |
En el segunda parte incluiremos el menú y en caso de tener un segundo nivel le asignaremos la clase .dropdown y anidaremos otro elemento <ul> dentro del elemento que hará de padre.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<div class="menu"> <ul> <li class="home"><a href="https://www.artegrafico.net" title="artegrafico.net">artegrafico.net</a></li> <li><a href="#">Who am i</a></li> <li class="dropdown"><a href="#">Projects</a> <ul> <li><a href="#">2019</a></li> <li><a href="#">2018</a></li> <li><a href="#">2017</a></li> </ul> </li> <li><a href="#">Contact</a></li> </ul> </div> |
Maquetación con Sass
Para empezar con la maquetación con Sass vamos a tener en cuenta 3 pasos importantes:
- Necesitamos definir los breakpoints que nos van a permitir definir los tamaños para una medida concreta de pantalla. En este caso vamos a usar los breakpoints que usa BootStrap4. A mayores crearemos un @mixin que va a crear las Media Queries con el breakpoint que se le ha pasado.
- También vamos a incluir variables con los códigos colores que se aplicarán al menú para que sea más sencillo portarlo a otro proyecto.
- Y por último crearemos el menú usando Sass para luego poder compilar su código a CSS.
Creando breakpoints y @mixin
Los breakpoints que usa Bootstrap4 son los siguientes:
- xs (para teléfonos con medidas de hasta 768px de ancho)
- sm (para tablets con medidas igual o superior a 768px de ancho)
- md (para pequeñas pantallas con medidas igual o superior a 992px de ancho)
- lg (para pantallas con medidas igual o superior a 1200px de ancho)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// breakpoints media queries $breakpoints: ( 'xs': 576px, 'sm': 768px, 'md': 992px, 'lg': 1200px ); $orientations: ( 'portrait': portrait, 'landscape': landscape ); // mixim media queries @mixin media-query($breakpoint) { // check if the key exist into the map @if(map-has-key($breakpoints, $breakpoint)) { $value: map-get($breakpoints, $breakpoint); @media (max-width: $value){ @content; } } } |
En nuestro caso no vamos a complicarnos la vida y usaremos el breakpoint «md» para pantallas pequeñas. Por lo que para usar el @mixin y declarar propiedades para este dispositivo usaremos:
1 |
@include media-query(md){ /* propiedades */ } |
Incluyendo variables para gestionar los colores
1 2 3 4 5 6 7 |
/* colors menu */ $navbar-color: hsl(0, 0%, 93%); $navbar-home-color: hsl(0, 0%, 100%); $navbar-li-color: hsl(0, 0%, 32%); $navbar-li-responsive-color: hsl(0, 1%, 25%); $navbar-dropdown-li-hover: hsl(0, 0%, 41%); $navbar-responsive-icon: hsl(0, 0%, 28%); |
Creando el menú con Sass
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
#navbar { margin: 0; padding: 0; position: initial; width: 100%; margin-bottom: 10px; background: $navbar-color; z-index: 100; .content { @include media-query(md) { margin:0; padding:0; width: 100% !important; } } .container{ @include media-query(md) { max-width: 100% !important; } } .menu { display: block; width: 100%; @include media-query(md){ display: none; clear:both; } } .open { @include media-query(md) { color:#000; display: block; width: 100%; li{ display: list-item; padding: 10px 0; border-left: solid 5px #525252; ul li { @include media-query(md) { padding-bottom: 0; // border-bottom: dotted #eee 1px; margin-left: 20px; padding: 10px 0; border-left: 0; width: 88%; } } } } } ul { list-style: none; margin: 0; padding: 18px 10px 18px 0; background: linear-gradient(#545454, #676767); width: 100%; height: 60px; font-size: 0; @include media-query(md){ padding:0; height: auto; } li { position: relative; display: inline; padding: 20px; cursor: pointer; display: initial; text-transform: uppercase; font-size: 1rem; /* li a */ a { color :hsl(0, 0%, 100%); padding-left: 20px; } /* ul li */ @include media-query(md) { padding: 12px 20px; } /* ul li.dropdown */ &.dropdown { display: inline; @include media-query(md) { display: block; } } /* li a */ a { text-decoration: none; } /* li.home */ &.home { background: $navbar-home-color; @include media-query(md) { display: none; } a { color: #000; } &:hover { background: $navbar-color; } } /* li:hover */ &:hover { background: $navbar-li-color; > ul li :after { content: ''; } @include media-query(md) { padding-bottom: 0; background: $navbar-li-responsive-color; > ul { width: 100%; } } } /* ul li ul */ > ul { display: none; /* hide default */ z-index: 9999; position: absolute; background: $navbar-li-color; //background: linear-gradient(#5791bf, #739cb7); width: 100%; top: 60px; @include media-query(md){ position: static; width: 100%; transition: all 0.5s ease-in; } } /* ul li.dropdown add mark */ &.dropdown a:after { content: "\25BC"; /*x25B2 y x25BC */ color: #fff; margin: 0 0 0 12px; @include media-query(md) { position: absolute; right: 25px; color: #ededed; } } /* ul li ul */ &:hover > ul { display: table; } /* ul li ul */ ul { @include media-query(md) { margin-top: 10px; } li { display: inline; padding: 18px; a { padding-left: 0px; } &:hover { background: $navbar-dropdown-li-hover; @include media-query(md){ background: transparent; } } } } } } /* responsive sidebar */ .responsive { display: none; @include media-query(md) { display: block; li { float: left; &.home { background: $navbar-home-color; display: block; a { padding-left: 0px; } } &.icon { width: 60px; height: 48px; background: $navbar-responsive-icon; a { color: #fff; padding-left: 0; } } } } } } |
Incluyendo Interactividad con JavaScript
Necesitamos crear un evento que nos permita cambiar el estado del icono del menú responsive. Por lo que en caso de hacer click se cambiará el icono de este y añadirá una clase .open o .close para mostrar o ocultar el menú.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
icon = document.querySelector('.icon'); icon.addEventListener('click', function (evt) { evt.preventDefault(); x = document.querySelector(".menu"); iconLink = document.querySelector(".responsive .icon a"); if (iconLink.className == "close") { x.className = "menu open"; iconLink.className = "open"; iconLink.innerHTML = "⛌"; } else { x.className = "menu"; iconLink.className = "close"; iconLink.innerHTML = "☰"; } }) |
Por último crearemos un evento de scroll porque si el usuario hace scroll en la página el menú desaparecerá y nosotros no queremos privar del menú al usuario. Por lo que en caso de desplazarse más allá de los píxeles marcados, cambiaremos la propiedad display del menú a fixed (que lo hará fijo)
1 2 3 4 |
window.addEventListener("scroll", function () { // show fixed menu on user scroll (window.scrollY > 250) ? navbar.style.position = "fixed" : navbar.style.position = "initial"; }) |
Una prueba funcional se puede comprobar siguiendo el siguiente enlace