Cómo Crear un Menú de Navegación Desplegable con HTML5, CSS3 y JQuery
() translation by (you can also view the original English article)
En este tutorial, echaremos un vistazo y veremos qué podemos lograr con HTML5 y CSS3 al tratarse del elemento básico de los sitios web actuales: el humilde menú de navegación desplegable. También usaremos jQuery para manejar los efectos y agregar los toques finales para nosotros.
HTML5 aporta a la especificación un elemento <nav> dedicado que debe utilizarse como contenedor para cualquier estructura de navegación principal, como los menús principales de navegación vertical u horizontal, o una tabla de contenidos en la página, por ejemplo. IE por desgracia no es compatible con este nuevo elemento todavía, pero hay una solución simple que podemos utilizar, de la que estoy seguro todos ustedes son conscientes.
Utilizando CSS3 podemos acabar con lo que habría requerido el uso de varias imágenes de fondo y posiblemente un contenedor de envoltura adicional o dos y confiar (casi) puramente en algunas de las nuevas propiedades de estilo, como las esquinas redondeadas y sombras, por ejemplo, que están disponibles para los navegadores compatibles. Una vez más, no todos los navegadores (¡tos, IE!) admiten estas nuevas reglas, pero podemos proporcionar soluciones de reserva para los navegadores que no pueden manejar nuestros estilos.
Por cierto, Envato Market tiene una gran selección de elegantes menús CSS para elegir, como PickArt, una solución sencilla, limpia y elegante que está lista para usar e instalar por sólo $ 4.



También puede probar uno de los proveedores de servicios expertos en Envato Studio. Por ejemplo, Lukasz Czerwinski creará un plugin jQuery completamente nuevo para sus especificaciones por $ 115 dentro de siete días, con una calificación de satisfacción del 100%. Sus ofertas incluyen menús desplegables, galerías de medios integradas con sus servicios sociales, deslizadores y mucho más.



Si no puede encontrar lo que necesita, o si prefiere hacerlo usted mismo, siga leyendo para averiguar cómo.
Paso 1. Introducción
Necesitaremos una copia de la última versión de jQuery, versión 1.4.2 al momento de escribir, así como una copia de la versión actual (1.1) de la excelente biblioteca Modernizr, que usaremos para orientar los navegadores soportados con el CSS3 que utilizamos.
Cree una carpeta de proyecto para los archivos que vamos a crear en algún lugar en su máquina y lo llamamos nav, dentro de esta carpeta cree tres nuevas carpetas; una llamada js, una llamada css y otra llamada reserva. Asegúrese de que las copias de jQuery y Modernizr estén almacenadas en la carpeta js.
Paso 2. La Página Subyacente
Comience la codificación creando la siguiente página en su editor de código favorito:
1 |
<!DOCTYPE html>
|
2 |
<html lang="en"> |
3 |
<head>
|
4 |
<meta charset="utf-8"> |
5 |
<title>HTML5, CSS3 and jQuery Navigation menu</title> |
6 |
<link rel="stylesheet" href="css/nav.css"> |
7 |
<!--[if IE]>
|
8 |
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
9 |
<![endif]-->
|
10 |
</head>
|
11 |
<body class="no-js"> |
12 |
<nav id="topNav"> |
13 |
<ul>
|
14 |
<li><a href="#" title="Nav Link 1">Nav Link 1</a></li> |
15 |
<li>
|
16 |
<a href="#" title="Nav Link 1">Nav Link 2</a> |
17 |
<ul>
|
18 |
<li><a href="#" title="Sub Nav Link 1">Sub Nav Link 1</a></li> |
19 |
<li><a href="#" title="Sub Nav Link 2">Sub Nav Link 2</a></li> |
20 |
<li><a href="#" title="Sub Nav Link 3">Sub Nav Link 3</a></li> |
21 |
<li><a href="#" title="Sub Nav Link 4">Sub Nav Link 4</a></li> |
22 |
<li class="last"><a href="#" title="Sub Nav Link 5">Sub Nav Link 5</a></li> |
23 |
</ul>
|
24 |
</li>
|
25 |
<li><a href="#" title="Nav Link 1">Nav Link 3</a></li> |
26 |
<li><a href="#" title="Nav Link 1">Nav Link 4</a></li> |
27 |
<li><a href="#" title="Nav Link 1">Nav Link 5</a></li> |
28 |
</ul>
|
29 |
</nav>
|
30 |
<script src="js/jquery.js"></script> |
31 |
<script src="js/modernizr.js"></script> |
32 |
</body>
|
33 |
</html>
|
Guarde esto como nav.html en la carpeta nav. Empezamos con el doctype HTML5 mínimo, que nos permite especificar el tipo de documento en un cuarto del código que utilizábamos normalmente. También especificamos el idioma predeterminado y la codificación de caracteres; aunque el documento aún validará sin estas dos cosas, es una buena práctica incluirlas y la página activará las advertencias de validador si no se especifica el idioma predeterminado. A continuación, enlazamos a una hoja de estilo (crearemos esto a continuación) y usaremos un comentario condicional que apunte a IE para cargar el excelente script de html5.js de Remy Sharp si es necesario.
En el cuerpo de la página tenemos el elemento <nav> como el contenedor de una lista tradicional de enlaces, y hemos lanzado en un sub-menú como medida preventiva también. El elemento no crea mágicamente un menú de navegación para nosotros, y no incluye ningún elemento de item de menú nuevo o algo por el estilo, por lo que una lista no ordenada sigue siendo una opción apropiada. El elemento <nav> es sólo un contenedor semántico para nuestro menú, el cual describe su función dentro del documento, para reemplazar el elemento genérico <div> que no indica nada inherentemente sobre su propósito en la página.
Al final del cuerpo enlazamos a nuestros archivos de script jQuery y Modernizr. Usaremos jQuery un poco más adelante cuando añadamos el comportamiento para el menú, pero Modernizr hará lo suyo inmediatamente, detectando las capacidades del navegador en uso y agregando una serie de nombres de clase al elemento HTML, que podemos usar para agregar nuestro CSS3 para que solo se aplique a navegadores que puedan hacer uso de ellos. También hemos añadido el nombre de clase no-js al <body> de la página; lo eliminaremos más tarde si JavaScript está habilitado de tal manera que también podemos utilizarlo para agregar estilos que sólo se deben aplicar cuando JavaScript está deshabilitado.
Paso 3. Algunos Estilos Básicos
Ahora vamos a añadir algunos estilos básicos; cree la siguiente hoja de estilos:
1 |
/* JS disabled styles */
|
2 |
.no-js nav li:hover ul { display:block; } |
3 |
|
4 |
/* base nav styles */
|
5 |
nav { display:block; margin:0 auto 20px; border:1px solid #222; position:relative; background-color:#6a6a6a; font:16px Tahoma, Sans-serif; } |
6 |
nav ul { padding:0; margin:0; } |
7 |
nav li { position:relative; float:left; list-style-type:none; } |
8 |
nav ul:after { content:"."; display:block; height:0; clear:both; visibility:hidden; } |
9 |
nav li a { display:block; padding:10px 20px; border-left:1px solid #999; border-right:1px solid #222; color:#eee; text-decoration:none; } |
10 |
nav li a:focus { outline:none; text-decoration:underline; } |
11 |
nav li:first-child a { border-left:none; } |
12 |
nav li.last a { border-right:none; } |
13 |
nav a span { display:block; float:right; margin-left:5px; } |
14 |
nav ul ul { display:none; width:100%; position:absolute; left:0; background:#6a6a6a; } |
15 |
nav ul ul li { float:none; } |
16 |
nav ul ul a { padding:5px 10px; border-left:none; border-right:none; font-size:14px; } |
17 |
nav ul ul a:hover { background-color:#555; } |
18 |
Save this file in the css directory as nav.css. The first rule is purely for when JavaScript is disabled, and allows the hidden submenu to be displayed on hover purely with CSS. The rest of the code defines a set of base styles that format the <nav> menu in the way that we want without adding anything too decorative. Note that we’re using the :after pseudo-selector to clear the floated list items; normally this would be added using a class name to that it could be applied to the containers of any floated elements on the page. At this point our page should look like this: |

Paso 4. CSS3
A continuación podemos agregar nuestro CSS3:
1 |
/* CSS3 */
|
2 |
.borderradius nav { -moz-border-radius:4px; -webkit-border-radius:4px; border-radius:4px; } |
3 |
.cssgradients nav { background-image:-moz-linear-gradient(0% 22px 90deg, #222, #999); background-image:-webkit-gradient(linear, 0% 0%, 0% 70%, from(#999), to(#222)); } |
4 |
.boxshadow.rgba nav { -moz-box-shadow:2px 2px 2px rgba(0,0,0,.75); -webkit-box-shadow:2px 2px 2px rgba(0,0,0,.75); box-shadow:2px 2px 2px rgba(0,0,0,.75); } |
5 |
.cssgradients nav li:hover { background-image:-moz-linear-gradient(0% 100px 90deg, #999, #222); background-image:-webkit-gradient(linear, 0% 0%, 0% 100%, from(#222), to(#555)); } |
6 |
.borderradius nav ul ul { -moz-border-radius-bottomleft:4px; -moz-border-radius-bottomright:4px; -webkit-border-bottom-left-radius:4px; -webkit-border-bottom-right-radius:4px; border-bottom-left-radius:4px; border-bottom-right-radius:4px; } |
7 |
.boxshadow.rgba nav ul ul { background-color:rgba(0,0,0,0.8); -moz-box-shadow:2px 2px 2px rgba(0,0,0,.8); -webkit-box-shadow:2px 2px 2px rgba(0,0,0,.8); box-shadow:2px 2px 2px rgba(0,0,0,.8); } |
8 |
.rgba nav ul ul li { border-left:1px solid rgba(0,0,0,0.1); border-right:1px solid rgba(0,0,0,0.1); } |
9 |
.rgba nav ul ul a:hover { background-color:rgba(85,85,85,.9); } |
10 |
.borderradius.rgba nav ul ul li.last { border-left:1px solid rgba(0,0,0,0.1); border-bottom:1px solid rgba(0,0,0,0.1); -moz-border-radius-bottomleft:4px; -moz-border-radius-bottomright:4px; -webkit-border-bottom-left-radius:4px; -webkit-border-bottom-right-radius:4px; border-bottom-left-radius:4px; border-bottom-right-radius:4px; } |
11 |
.csstransforms ul a span { -moz-transform:rotate(-180deg);-webkit-transform:rotate(-180deg); } |
Usando las clases agregadas al elemento <html> por Modernizr podemos agregar de forma fácil y segura los estilos CSS3 que queremos. Utilizamos el estilo border-radius para dar al elemento <nav> esquinas redondeadas; Tenemos que darle a Mozilla y a Webkit declaraciones de estilo prefijadas, así como los estilos estándar de border-radius para navegadores que los soportan, como Opera. Tenemos que hacer esto con la mayoría de nuestros estilos CSS3.
Así como el redondeo de esquinas del elemento <nav> también le damos un degradado y una sombra. Los estilos de degradado son bastante complejos y son diferentes para los navegadores basados en Mozilla y Webkit, los cuales son los únicos navegadores que los implementan actualmente. Ambos navegadores utilizan la propiedad background-image. En Firefox utilizamos -moz-linear-gradient para añadir un degradado lineal. Requiere valores que correspondan con punto inicial del degradado (0%), el punto en el que el primer color se funde en el segundo color (22px), el ángulo del eje del gradiente (90deg), el primer color (# 999) y el segundo color (# 222).
Podemos obtener el mismo degradado en Safari o Chrome utilizando -webkit-gradient y la sintaxis es sutilmente diferente; especificamos que debe ser un degradado lineal y, a continuación, proporcionamos dos puntos que le dicen al navegador donde el degradado debe comenzar y terminar. Los valores en el ejemplo corresponden a los valores de izquierda, superior y derecha de 0% y 70% para abajo, lo que nos da el mismo estilo que el utilizado en Firefox. Por último, especificamos los colores del degradado.
Cuando aplicamos el drop-shadow lo combinamos con la clase Modernizr para RGBA así como boxshadow para que nuestra sombra pueda ser transparente. Las propiedades para Mozilla y webkit son las mismas, y también proporcionamos la propiedad real box-shadow para los navegadores permitidos. Los valores que especificamos para esta regla son el offset izquierdo (2px), el offset superior (2px), la cantidad de borrosidad (2px) y finalmente el color de la sombra (0,0,0). El color es donde usamos RGBA, lo que nos permite establecer la opacidad al 75% (.75).
Otro estilo interesante que utilizamos es transform para girar un poco de texto 180 grados; cuando escribamos el script en un momento, veremos que agregamos un indicador de submenú en forma de signo de flecha a cualquier elemento de lista que contenga un submenú - este estilo girará el carácter hacia el que apunte hacia abajo, lo que significa que en navegadores aceptados ni siquiera necesitamos usar una imagen para esta función.
Las reglas restantes establecen diferentes degradados, bordes redondeados, opacidad con RGBA y sombras en otros elementos del menú <nav>, como esquinas redondeadas agradables y una sombra en el submenú, además de invertir el degradado para un atractivo estado hover . Ahora nuestro menú de navegación debería verse así en un navegador compatible:

En navegadores compatibles podemos hacer que nuestros elementos se vean muy calientes sin usar una sola imagen, lo que significa que nuestras páginas se cargarán mucho más rápido con mucho menos solicitudes HTTP. Sin embargo, no todos los navegadores soportarán el estilo CSS3, especialmente cualquier versión de IE, por lo que todavía tenemos que definir nuestros estilos de respaldo. Agregue el código siguiente a la hoja de estilo:
1 |
/* fallbacks */
|
2 |
.no-cssgradients nav, .no-js nav { padding-bottom:4px; border:none; background:url(../fallback/navBG.gif) repeat-x 0 0; } |
3 |
.no-borderradius nav ul, .no-js nav ul { background:url(../fallback/navRight.gif) no-repeat 100% 0; } |
4 |
.no-borderradius nav ul ul, .no-js nav ul ul { background:none; } |
5 |
.no-borderradius nav li, .no-js nav li { height:44px; } |
6 |
.no-cssgradients nav li:hover, .no-js nav li:hover { background:url(../fallback/navOverBG.gif) repeat-x 0 0; } |
7 |
.no-borderradius nav li li, .no-js nav li li { height:auto; width:98%; left:-2px; } |
8 |
.no-borderradius nav li:first-child, .no-js nav li:first-child { background:url(../fallback/navLeft.gif) no-repeat 0 0; } |
9 |
.no-borderradius nav li:first-child:hover, .no-js nav li:first-child:hover { background:url(../fallback/navOverLeft.gif) no-repeat 0 0; } |
10 |
.no-borderradius nav li li:first-child, .no-js nav li li:first-child { background:none; } |
11 |
.no-rgba nav ul ul, .no-js nav ul ul { left:1px; padding-left:2px; background:url(../fallback/subnavBG.png) no-repeat 100% 100%; } |
12 |
.no-rgba nav ul ul a, .no-js nav ul ul a { left:3px; } |
13 |
.no-rgba nav ul ul a:hover { background:url(../fallback/subOverBG.png) repeat 0 0; } |
14 |
.no-csstransforms ul a span { height:7px; width:12px; margin-top:8px; text-indent:-5000px; overflow:hidden; background:url(../fallback/indicator.png) no-repeat 0 0; } |
15 |
.no-borderradius ul ul li.last { margin-bottom:10px; } |
16 |
.no-cssgradients.boxshadow nav { box-shadow:none; } |
Modernizr también agregará nombres de clase que muestran las características CSS3 que no están disponibles para el navegador, por lo que podemos suministrar fácilmente reglas alternativas, que hacen uso de respaldos basados en imágenes donde las características no son compatibles, así como cualquier estilo que podamos necesitar como resultado de utilizar las imágenes.
Usted notará que también usamos selectores que apuntan a nuestra clase no-js aquí también; esto es porque cuando JavaScript está deshabilitado, Modernizr no se ejecutará y no agregará los nombres de clase que necesitamos para el documento, por lo que nuestros respaldos no-CSS3 también se convierten en nuestros respaldos no-js.
Paso 5. Añadiendo el Script
Ahora vamos a añadir algún script. Lo primero que debemos hacer es eliminar la clase no-js del cuerpo de la página cuando JavaScript no está deshabilitado. Queremos hacer esto tan pronto como sea posible en el proceso de renderizado de página para evitar un parpadeo cuando se cambian los estilos. Directamente después de la etiqueta de apertura del body agregue el siguiente código:
1 |
<script> |
2 |
var el = document.getElementsByTagName("body")[0]; |
3 |
el.className = ""; |
4 |
</script> |
Todo lo que hacemos es obtener el elemento <body> por nombre de etiqueta y establecer su propiedad className como una cadena vacía. Normalmente utilizaríamos jQuery para hacer eso por nosotros, pero como jQuery no se cargará cuando se ejecute este script, no podemos usarlo. Podríamos cargar jQuery antes de esto por supuesto, pero entonces encajaríamos un golpe de rendimiento masivo. Nuestro script sólo tiene 2 líneas de código por lo que no causará un retraso significativo, y porque se ejecutará antes de que el navegador incluso haya procesado el marcado para el elemento <nav>, no habrá flash de contenido sin estilo.
Ahora que la clase ha sido eliminada del cuerpo, nuestros submenús CSS ya no funcionarán, así que podemos añadir este comportamiento de nuevo con jQuery y mejorarlo un poco al mismo tiempo. Al final del documento, inmediatamente después del script de referencia para Modernizr agregue el siguiente código:
1 |
<script> |
2 |
(function($){ |
3 |
|
4 |
//cache nav |
5 |
var nav = $("#topNav"); |
6 |
|
7 |
//add indicators and hovers to submenu parents |
8 |
nav.find("li").each(function() { |
9 |
if ($(this).find("ul").length > 0) { |
10 |
|
11 |
$("<span>").text("^").appendTo($(this).children(":first")); |
12 |
|
13 |
//show subnav on hover |
14 |
$(this).mouseenter(function() { |
15 |
$(this).find("ul").stop(true, true).slideDown(); |
16 |
}); |
17 |
|
18 |
//hide submenus on exit |
19 |
$(this).mouseleave(function() { |
20 |
$(this).find("ul").stop(true, true).slideUp(); |
21 |
}); |
22 |
} |
23 |
}); |
24 |
})(jQuery); |
25 |
</script> |
El script es relativamente sencillo; envolvemos nuestro código dentro de un closure y pasamos el objeto jQuery con seguridad con su nombre espaciado al signo de dólar, sólo en caso de que otra biblioteca está en uso cuando el código se ponga en producción. A continuación, almacenamos en caché una referencia al elemento <nav> para que podamos referirnos a él sin seleccionarlo del documento repetidamente. Luego procesamos cada elemento de la lista dentro del menú.
Para cada elemento coincidente, lo comprobamos para ver si contiene elementos <ul> anidados y si ese es el caso agregamos un nuevo elemento <span> al elemento de lista. Esto se convertirá en nuestro indicador de submenú. Cuando se encuentra un submenú también adjuntamos las ayudas de evento mouseenter() y mouseleave() al elemento de lista que contiene el menú. Todas estas ayudas muestran y ocultan el submenú deslizándolo hacia abajo o hacia arriba respectivamente. Tenga en cuenta el uso del método stop() que ayuda a evitar que las animaciones de apertura y cierre se pongan en fila si el puntero del ratón se mueve repetidamente dentro y fuera de los elementos de lista de destino.
En este punto debemos estar en un lugar bastante agradable con respecto a la mayoría de las situaciones; en cualquier navegador que admita HTML5, nuestro menú debería verse relativamente similar independientemente de si CSS3 está soportado o no, y si el scripting está habilitado o no. Sin embargo, IE nos presenta un problema; cuando JS está habilitado, el script htmlshiv.js hace que IE entienda el elemento <nav> y nuestros estilos no-css3 son recogidos e implementados - todo muy bien (todavía tenemos algunos problemas con IE7, ya que entre otras cosas las reglas de nuestro clear automático :after no se entienden ni se aplican, pero llegaremos a ellos en un momento).
Sin embargo, los problemas comienzan cuando se utiliza IE con scripting desactivado - en esta situación, el script html5shiv.js no se ejecuta y IE no entiende el elemento <nav>. ¡Ninguno de nuestros selectores que incluyan nav en ellos será implementado! Pero no es el fin del mundo; podemos proporcionar una hoja de estilos alternativa que sólo se utiliza cuando el navegador tiene JS desactivado y es IE. Directamente después de la secuencia de comandos que elimina la clase no-js del elemento <body>, añada lo siguiente:
1 |
<noscript>
|
2 |
<!--[if IE]>
|
3 |
<link rel="stylesheet" href="css/ie.css">
|
4 |
<![endif]-->
|
5 |
</noscript>
|
Una solución sencilla de hecho. Ahora necesitamos crear la nueva hoja de estilos; agregue las siguientes reglas a un nuevo documento en su editor de código:
1 |
/* ie styles for when js disabled */
|
2 |
ul { display:block; padding:0; margin:0; background:url(../fallback/navRightIE.gif) no-repeat 100% 0; font:16px Tahoma, Sans-serif; } |
3 |
ul:after { content:"."; display:block; height:0; clear:both; visibility:hidden; } |
4 |
li { height:44px; position:relative; float:left; list-style-type:none; background:url(../fallback/navBG.gif) repeat-x 0 0; } |
5 |
li.last a { border-right:none; } |
6 |
li:hover { background:url(../fallback/navOverBG.gif) repeat-x 0 0; } |
7 |
li:first-child { background:url(../fallback/navLeftIE.gif) no-repeat 0 0; } |
8 |
li:first-child a { border-left:none; } |
9 |
li:first-child:hover { background:url(../fallback/navOverLeft.gif) no-repeat 0 0; } |
10 |
li a { display:block; padding:10px 20px; border-left:1px solid #999; border-right:1px solid #222; color:#eee; text-decoration:none; } |
11 |
li li { width:auto; clear:left; } |
12 |
li li:first-child { background:none; } |
13 |
li li:hover { background-image:none; } |
14 |
ul li li a:hover { border-right:none; } |
15 |
ul ul { display:none; padding-left:2px; position:absolute; left:2px; background:url(../fallback/subnavBG.png) no-repeat 100% 100%; } |
16 |
ul li:hover ul { display:block; } |
17 |
li li { height:auto; width:98%; left:-2px; } |
18 |
ul ul a:hover { background:url(../fallback/subOverBG.png) repeat 0 0; } |
19 |
ul a span { height:7px; width:12px; margin-top:8px; text-indent:-5000px; overflow:hidden; background:url(../fallback/indicator.png) no-repeat 0 0; } |
20 |
ul ul li { background:none; } |
21 |
ul ul li.last { margin-bottom:10px; } |
22 |
ul ul li a { padding:5px 10px; border-left:0; left:3px; font-size:14px; white-space:pre; } |
Guárdelo en la carpeta css como ie.css. Como puede ver, no estamos apuntando al elemento <nav> para nada en esta hoja de estilos. Algunos estilos que se han agregado anteriormente al elemento <nav> se han añadido al elemento <ul> en su lugar, y hay algunos nuevos estilos que deben incluirse específicamente para este escenario. Esencialmente, sin embargo, la hoja de estilo crea el mismo efecto que antes, por lo que IE8 con JS desactivado debe seguir apareciendo así:

Hemos tenido que hacer uso de un par de imágenes más para este escenario, ya que ya no tenemos el elemento <nav> para colgar el background repeat para el degradado principal. Así que eso es todo, los navegadores modernos, con JS habilitado y deshabilitado, funcionando como se espera - utilizando CS3 donde sea posible y la imagen de respaldo donde no lo sea.
IE7 seguirá creándonos problemas, incluso con scripts habilitados, pero podemos arreglarlo fácilmente usando otro comentario condicional para dirigir específicamente a IE7, y proveyendo una nueva hoja de estilo solo para IE7 que corrige los problemas de disposición de diseño; algo como esto es todo lo que necesitamos:
1 |
* styles to fix IE7 */ |
2 |
ul { display:inline-block; } |
3 |
ul li a span { position:absolute; right:5px; top:10px; } |
4 |
ul ul li a { border-right:none; padding:5px 10px; } |
5 |
.content { clear:both; } |
Guárdelo en la carpeta css como ie7.css y añada un nuevo comentario condicional en el <head> de la página:
1 |
<!--[if IE 7]>
|
2 |
<link rel="stylesheet" href="css/ie.css">
|
3 |
<link rel="stylesheet" href="css/ie7.css">
|
4 |
<![endif]-->
|
Ahí lo tenemos; un menú de navegación construido y estilizado con los últimos elementos y estilos con respaldos y arreglos para navegadores antiguos.