Explicamos cómo crear un menú desplegable animado sin necesidad de JavaScript


Los menús desplegables son una interfaz de navegación muy común. Hasta ahora con CSS podíamos hacer que los vínculos de las subsecciones aparecieran sin necesidad de JavaScript, pero si queríamos hacer que el efecto de dicha aparición fuera progresivo, no nos quedaba más opción que programar una animación. Con CSS3, podemos definir el efecto en la hoja de estilo.

Hace ya casi tres años publiqué una prueba de -webkit-transition, una propiedad que permitía especificar tiempos y modos de transición entre dos valores. Los experimentos de Webkit fueron la base del modulo de transiciones de CSS nivel 3, con el que podemos crear pequeñas animaciones.

¿Animaciones? ¿Cómo?

Lo que hacen los navegadores que soportan el módulo de transición es interpolar los pasos intermedios entre el valor de una o varias propiedades de CSS aplicadas a un elemento —o los pseudoelementos :before y :after— y el valor asignado al mismo cuando se cumplen las condiciones que hayamos elegido, por ejemplo, cuando un usuario interactúa con el elemento y se cambia la propiedad en :hover.

La propiedades de transición —que, por cierto, no se heredan— son las siguientes:

  • transition-property: indica la propiedad de CSS que queremos animar. No todas las propiedades pueden someterse a transiciones, pero ésta es la lista de las que sí. Su valor por defecto es all, es decir, que si no se especifica ninguna propiedad, la transición se aplicará a todas las del elemento.
  • transition-duration: indica el tiempo que debe durar la animación, y se puede indicar en segundos (s) o milisegundos (ms). Si no se indica una duración, la transición es instantánea.
  • transition-timing-function: indica la manera en la que deben calcularse los valores intermedios de la transición, y se expresa por medio de una curva cúbica de Bézier, con el valor cubic-bezier(a,b,c,d), donde a, b, c y d son las coordenadas de P1 y P2, con valores entre 0 y 1; P0 y P3 son siempre (0,0) y (1,1) respectivamente.

    No obstante, con buen criterio, la especificación ya define las más comunes por medio de palabras clave:

    • ease: el valor por defecto, equivalente a cubic-bezier(0.25,0.1,0.25,1.0). Traducido quiere decir que la animación es un poco más lenta al comienzo y al final.
    • linear: equivalente a cubic-bezier(0.0,0.0,1.0,1.0), la progresión del valor de la propiedad animada es aritmética, por lo que la transición es uniforme.
    • ease-in: equivale a cubic-bezier(0.42,0,1.0,1.0); la animación acelera su velocidad a medida que progresa.
    • ease-out: equivale a cubic-bezier(0,0,0.58,1.0); la animación decelera su velocidad a medida que progresa.
    • ease-in-out: equivale a cubic-bezier(0.42,0,0.58,1.0); la animación primero acelera hasta la mitad de su duración y luego decelera. Se diferencia de ease en que esta última acelera al principio, se mantiene un intervalo constante y luego decelera.
  • transition-delay: si no queremos que la animación comience inmediatamente, podemos asignarle un retraso inicial, medido también en segundos (s) o milisegundos (ms).

Además, contamos con una propiedad abreviada, que es transition, donde podemos indicar todos estos valores separados por espacios. Por ejemplo, en esta declaración:


transition: background-color 0.5s linear 0.25s;
 

los valores corresponden a la propiedad a animar, la duración de la animación, su tipo y el retraso inicial.

Genial. Pero antes de pasar a explicar cómo podemos emplear todo esto para hacer un menú desplegable, vamos a indicar unos cuantos puntos a tener en cuenta al trabajar con animaciones.

Algunas notas

¿Dónde indicar la transición?

Primero y más importante, las propiedades de transición se deben especificar en el elemento que va a sufrirlas. Parece obvio, pero al principio puede ser un tanto contraintuitivo.

Imaginemos que tenemos un vínculo en una barra de navegación y que queremos que su color de fondo cambie de manera progresiva cuando el usuario pase el ratón sobre él. Estas reglas…


a{
  background-color: #D0D0D0;
}

a:hover{
  background-color: #FF4040;
  transition-duration: .5s;
}
 

no funcionan. Las correctas son estas:


a{
  background-color: #D0D0D0;  
  transition-duration: .5s;
}

a:hover{
  background-color: #FF4040;
}
 

¿Se pueden indicar parámetros independientes para varias propiedades?

Sí, de varias formas, dependiendo de lo que resulte más cómodo para el autor.

Primero, podemos especificar cada propiedad con los distintos valores separados por comas:


a{
  color: #FFF;
  background: #333;
  transition-property: color, background;
  transition-duration: .5s, .75s;
  transition-timing-function: ease-in-out;
  transition-delay: .25s, 0;
}
 

Los valores se asignan en el orden en que se indican. Si faltan valores —como aquí en transition-timing-function— estos se asignan por orden a las propiedades hasta agotarse y los restantes reciben los valores por defecto.

La segunda opción es especificar las dos transiciones abreviadas:


a{
  color: #FFF;
  background: #333;
  transition: color .5s ease-in-out .25s;
  transition: background .75s;
}
 

En este caso no especificamos para el fondo ni el tipo de animación ni el retardo, dado que sus valores son los aplicados por defecto.

Y, por último, podemos especificar todos los valores en una sola transición, separando cada animación por comas:


a{
  color: #FFF;
  background: #333;
  transition: color .5s ease-in-out .25s, background .75s;
}
 

¿Y el soporte?

En el momento en que escribo, ninguno de los navegadores actuales ha implementado las propiedades estándar del módulo de transiciones, pero algunos sí soportan las versiones experimentales con los prefijos propietarios:

  Prefijo Desde la versión
Firefox -moz- 4
Safari -webkit- 3.1
Safari (iOS) -webkit- 3.2
Chrome -webkit- 4.0
Opera -o- 10.5
Explorer De momento no hay soporte alguno

Nuestro ejemplo

El menú de navegación consta de una serie de listas anidadas con los vínculos. En el ejemplo hemos empleado muchas otras propiedades de CSS3, pero vamos a ceñirnos estrictamente a la animación del menú:


#navegacion li ul{
  height:0;
  overflow:hidden;
  -moz-transition-duration:.3s;
  -webkit-transition-duration:.3s;
  -o-transition-duration:.3s;
  transition-duration:.3s;
  position:absolute;
}
#navegacion li:hover ul{
  height:17em;
}
 

Simplemente variamos la altura, de 0 a 17em. ¿Y por qué no simplemente cambiar el valor a auto? Buena pregunta. En un principio es lo que probamos, pero parece que la animación no funciona si no se especifica un valor en alguna de las unidades de medida.

En la especificación hay una sección que indica cuando no debe iniciarse una animación, pero no parece que deba ser aplicable en este caso. Tal vez se trate de un bug —ya está reportado como tal en Bugzilla—, pero lo cierto es que ninguno de los navegadores que soportan transiciones animan el elemento con height:auto.

¿CSS o JavaScript?

Hace años ya que hay cierta controversia sobre si una animación pertenece a la capa de presentación o a la de comportamiento —este artículo de Jonathan Snook es de 2007—. Personalmente entiendo que la capa de comportamiento, léase JavaScript, tiene que ver con la funcionalidad que añade a una página —validación de formularios, manipulación del DOM, etc.—, y no tanto con la interpolación entre dos valores relativos al aspecto de la presentación de un elemento. Incluso el W3C las define así: «Transitions are a presentational effect».

Pero no sólo se trata de una cuestión más o menos académica: las transiciones de CSS son más eficientes. Al soportarlas el navegador de forma nativa las animaciones pueden beneficiarse de la aceleración por hardware del disposivo en el que el navegador esté instalado, reducen el consumo de recursos por parte del motor de JavaScript y, lo más importante, como este lenguaje es «mono-hilo» (single-threaded) el efecto no interfiere con otras funciones.

Así pues, viendo que además degrada muy elegantemente, no hay motivo para no empezar a utilizar las transiciones ya mismo.

Esta entrada se publicó el 31 de julio de 2011, se archivó en , y fue etiquetada como animaciones, css3. Autor: Saúl González Fernández. Hay 9 comentarios ›.

Comentarios

  1.  liliana dice:

    Muy bien explicado, por fin encontré una página con los detalles que necesitaba. Gracias.

  2.  Sato dice:

    Excelente blog, muy bien explicado. Si hay alguna manera de ayudar para difundir tu blog no dudes en hacérmelo saber.

  3.  Mauricio dice:

    Bien explicado, la unica duda que tengo con respecto al menú, es cómo hacer que se superponga al texto, o sea, que el despliegue se haga por encima del contenido. Gracias.

  4.  Saúl González Fernández dice:

    @Mauricio: Prueba asignar una posicion relativa al elemento en el que incluyes la navegación y al del contenido; después al primero especifícale un z-index superior al del segundo.

  5.  el Sindrome de Willy Fog dice:

    Hola.

    Estoy intentando que cuando el menu se despliege, lo haga mas lento, es decir, que el submenu me aparezca a una velocidad más lenta, y si puede ser, cada linea a un tiempo.

    No sé muy bien dónde aplicar la transición, ya que sí que consigo aplicarla para que al pasar el raton por encima, la pestaña cambie de color más lentamente, pero no para ese efecto que deseo.

    Os copio el texto a ver si me decís dónde ponerlo.

    Gracias.

    
    #menuWrapper {
      width:100%; /* Ancho del menú */
      height:35px;
      padding-left:14px;
      background:#333333; /* Color de fondo */
      border-radius:20px; /* Bordes redondeados */
    }
    .menu {
      width: 100%;
      float: left;
      font-family:"Lucida Sans Unicode", "Trebuchet Unicode MS", "Lucida Grande",sans-serif;
      font-size:13px; /* Tamaño de la fuente */
      font-weight:bold;
    }
    .menu ul {
      float:left;
      height:0px;
      list-style:none;
      margin:0;
      padding:0;
      border-radius: 0px 0px 20px 20px; /* Bordes redondeados del submenú */
    }
    .menu li{
      float:left;
      padding:0px;
    }
    .menu li a{
      background:#333333 url(http://lh4.googleusercontent.com/-iqgCHipLGx4/T5DqLrsVALI/AAAAAAAACaI/bcIY7SV8s5I/s35/separador.gif) bottom right no-repeat; 
      color:#cccccc; /* Color de la fuente */
      display:block;
      font-weight:normal;
      line-height:35px;
      margin:0px;
      padding:0px 25px; /* Espacio entre cada pestaña */
      text-align:center;
      text-decoration:none;
    }
    .menu li a:hover, .menu ul li:hover a {
      background: #2580a2; /* Color de las pestañas al pasar el cursor */
      color:#FFFFFF; /* Color del texto al pasar el cursor */
      text-decoration:none;
    }
    .menu li ul {
      background:#333333; /* Color de fondo del submenú */
      display:none;
      height:auto;
      padding:0px;
      margin:0px;
      position:absolute;
      width:200px; /* Ancho del submenú */
      z-index:100;
      border-top:1px solid #fff; /* Borde superior del submenú */
    }
    .menu li:hover ul{
      display:block;
    }
    .menu li li {
      display:block;
      float:none;
      margin:0px;
      padding:0px;
      width:200px;
    }
    .menu li:hover li a {
      background:none;
      border-radius: 0px 0px 20px 20px; /* Borde de las subpestañas */
    }
    .menu li ul a {
      display:block;
      height:35px;
      font-size:12px;
      font-style:normal;
      margin:0px;
      padding:0px 10px 0px 15px;
      text-align:left;
    }
    .menu li ul a:hover, .menu li ul li:hover a{
      background:#2580a2; /* Color de las subpestañas al pasar el cursor */
      color:#ffffff;
      text-decoration:none;
    }
     
  6.  Saúl González Fernández dice:

    @el Síndrome de Willy Fog: Nos resultaría más fácil verlo si nos envías una dirección donde podamos echar un vistazo a la página, pero creemos que lo que necesitas es asignar estas propiedades:

    
    .menu li ul {
      /* Aquí tu código original, con las indicaciones que te damos debajo */ 
      height:0;
      overflow:hidden;
      -moz-transition-duration:.3s;
      -webkit-transition-duration:.3s;
      -o-transition-duration:.3s;
      transition-duration:.3s;
    }
     

    Sustituye la altura por 0 en lugar de auto y elimina display:none. En el código la duración que hemos puesto es .3s, sustitúyela por la que creas apropiada.

    
    .menu li:hover ul{
      height: 10em; /* Valor a estimar */
    }
     

    Para esta regla, elimina display:block, y dale la altura que necesites para que se vean todas las opciones del menú.

    Prueba y nos comentas.

  7.  Eduardo dice:

    Hola Saúl,
    No soy el tío que preguntó sobre como hacer que las listas desplegables de su menú salgan más lento pero, déjame decirte que me has salvado de muchas horas más de dolor de cabeza XD. Yo he estado intentando hacer que las listas de mi menú se desplieguen lento y no había podido desde hace más de una semana, no miento. Me dí cuenta desde hace 7 días que el efecto TRANSITION no afecta a los DISPLAY, a pesar de eso intentaba e intentaba todo terco pero no conseguía algo; no se me había ocurrido cambiar el tamaño de las listas desplegables para que funcione el efecto, como tú lo has explicado… muchas gracias por el código, me sirvió mucho…

  8.  Pitux dice:

    Muchas gracias, por fin entendí que la propiedad display no acepta transiciones.

  9.  Fabricio Trujillo dice:

    :O ¡Muchas gracias, me ha servido mucho! Mil gracias ?

Un 93,2% de españoles reconoce tener problemas con su WiFi

Un 93,2% de españoles reconoce tener problemas con su WiFi

– La mayoría de los usuarios de Internet de nuestro país ha sufrido alguna vez problemas relacionados con el WiFi, según un estudio realizado por devolo en julio de 2021 que refleja el estado y la percepción de los españoles respecto a su conexión a Internet en el hogar. El objetivo de dicho estud

 ScrollTo en menos de 2Kb

ScrollTo en menos de 2Kb

Siguiendo con nuestra iniciativa de replicar efectos de animación populares de JavaScript en menos de 2Kb, creamos un ScrollTo que no depende de ninguna librería. El efecto Habrá visto en muchos sitios web —sobre todo en los que consisten en una única página— un efecto que consiste en que al ha

Responsive images sin JavaScript ni PHP

Responsive images sin JavaScript ni PHP

A día de hoy, responsive design arroja 77 millones de resultados aproximadamente de Google, pero a pesar de las virtudes y la relativa facilidad con la que crear un diseño adaptativo, hay un bestia negra que plantea un problema: las imágenes. Pensando en ello hemos hecho un pequeño experimento. El problema reside en

Destacar y atenuar elementos simultáneamente con CSS3

Destacar y atenuar elementos simultáneamente con CSS3

En algún blog nos hemos encontrado con un efecto my sutil, pero que nos ha parecido muy elegante: ver una lista de elementos como comentarios, tweets o entradas relacionadas, que al pasar el ratón por encima reaccionan oscureciendo el ítem que recibe el :hover, y atenuando los demás. En muchos casos se trata de un efecto

MapBox: una alternativa al mapa de Google

MapBox: una alternativa al mapa de Google

Llevábamos unas semana buscando un rato libre para probar MapBox, y por fin lo hemos encontrado: El servicio nos permite generar un mapa con la misma funcionalidad que nos ofrece Google Maps, pero con la interesante adición de poder alterar aspectos como la paleta de colores, iconos y demás, de manera que se pueda ajustar m&

Clientes

Nos valoran 9,3 sobre 10 La satisfacción de nuestros clientes es nuestro objetivo, y su evaluación es lo más provechoso que nos aportan. Por ello, cuando terminamos un proyecto les pedimos que puntúen una serie de aspectos de nuestro trabajo que pueden servir de referencia para nuestros futuros clientes. Como con

Outline y los bordes redondeados

La propiedad outline dibuja alrededor de un elemento una línea a modo de marco que puede emplearse como un segundo borde a efectos de estilo. Sin embargo, esta línea no sigue el contorno de dicho elemento si a éste se le asigna un radio de borde para redondear las esquinas. En esta entrada muestro un par de soluciones a este pe

Carrusel de imágenes con efecto de fundido (y en menos de 2Kb)

No hace tanto, explicábamos cómo habíamos vuelto flexible un plugin de jQuery que consiste en un carrusel de imágenes. En esta entrada mostramos una versión que hemos programado desde cero, con un efecto diferente y que no depende de ninguna librería. El carrusel de imágenes no sólo es un e

Efectos javascript

Un acordeón fallido con CSS3 Creamos una interfaz de acordeón con un híbrido de transiciones de CSS3 y JavaScript. Carrusel con desplazamiento horizontal, en menos de 2… en lo menos que hemos podido Programamos un carrusel de contenidos con desplazamiento horizontal, flexible y casi en menos de 2Kb. ScrollTo,

Menús desplegables animados con CSS3

Los menús desplegables son una interfaz de navegación muy común. Hasta ahora con CSS podíamos hacer que los vínculos de las subsecciones aparecieran sin necesidad de JavaScript, pero si queríamos hacer que el efecto de dicha aparición fuera progresivo, no nos quedaba más opción que prog

Contrato diseño web

Contrato de diseño y desarrollo de sitio web INTERVIENEN   De una parte, D. [el nombre de nuestro representante], mayor de edad ycon N.I.F. 53022319-M, en calidad de [cargo en nuestra empresa] de DigitalIcon, Servicios Informáticos, S.L. (en adelante DIGITAL ICON), con C.I.F. B-84622927 y con domicilio social en Plaza Mayor