Crear una línea de tiempo con CSS y un poco de JavaScript
Spanish (Español) translation by Elías Nicolás (you can also view the original English article)
En este tutorial, aprenderemos cómo crear una línea de tiempo vertical responsive desde cero. En primer lugar, vamos a crear la estructura básica con un poco de HTML y el poder de los pseudo-elementos en CSS. Entonces, vamos a usar algo de JavaScript para añadir algunos efectos de transición al bajar en la página.
Esta es una idea de lo que vamos a construir (para ver más grande, ingrese a CodePen).
1. Estructura HTML
La estructura que vamos a usar es bastante sencilla; una lista desordenada con un elemento div
dentro de cada uno de nuestros artículos de la lista. Como
tratamos eventos a lo largo de una línea de tiempo, daremos a cada
elemento de lista un elemento time
para mostrar el año.
Además, vamos a envolver todo dentro de un elemento de section
con la clase timeline
:
<section class="timeline"> <ul> <li> <div> <time>1934</time> Some content here </div> </li> <!-- more list items here --> </ul> </section>
Esto nos da la siguiente salida sin estilar:
2. Añadiendo estilos CSS iniciales
Después
de algunos colores básicos, etc. (consulte la sección superior del CSS
en el pen de abajo) definiremos algunas reglas CSS estructurales para
los elementos de la lista. También diseñaremos el pseudo-elemento ::after
después de estos elementos:
.timeline ul li { list-style-type: none; position: relative; width: 6px; margin: 0 auto; padding-top: 50px; background: #fff; } .timeline ul li::after { content: ''; position: absolute; left: 50%; bottom: 0; transform: translateX(-50%); width: 30px; height: 30px; border-radius: 50%; background: inherit; }
He eliminado el contenido de los elementos de la lista para que este paso sea más claro, dándonos lo siguiente:
3. Estilos de elementos de la línea de tiempo
Ahora
vamos a diseñar los elementos div
(los llamaremos "elementos de línea
de tiempo" de ahora en adelante) que forman parte de los elementos de la
lista. Una vez más, el estilamos el pseudo-elemento ::before
de estos elementos.
Además, como veremos en un momento, no todas las divs comparten los mismos estilos. Gracias a las pseudo-clases CSS de :nth-child(odd)
y :nth-child(even)
, podemos diferenciar sus estilos.
Echa un vistazo a las reglas CSS correspondientes a continuación:
.timeline ul li div { position: relative; bottom: 0; width: 400px; padding: 15px; background: #F45B69; } .timeline ul li div::before { content: ''; position: absolute; bottom: 7px; width: 0; height: 0; border-style: solid; }
Entonces algunos estilos para nuestros elementos impares:
.timeline ul li:nth-child(odd) div { left: 45px; } .timeline ul li:nth-child(odd) div::before { left: -15px; border-width: 8px 16px 8px 0; border-color: transparent #F45B69 transparent transparent; }
Finalmente, los estilos de nuestros elementos pares:
.timeline ul li:nth-child(even) div { left: -439px; } .timeline ul li:nth-child(even) div::before { right: -15px; border-width: 8px 0 8px 16px; border-color: transparent transparent transparent #F45B69; }
Con estas reglas en su lugar (y nuestro HTML una vez más completo con el contenido) nuestra línea de tiempo se ve como sigue:
La principal diferencia entre los divs "odd" y "even" es su posición. Los primeros tienen left: 45px
; Mientras que los segundos tienen left:-439px
;. Para entender el posicionamiento de nuestros divs pares, vamos a hacer algunas matemáticas simples:
Ancho de cada div + Espacio deseado - Ancho de cada elemento de la lista = 400px + 45px - 6px = 439px
La segunda diferencia, menos importante, es la flecha generada de su pseudo-elemento. Esto significa que el pseudo-elemento de cada una de las divs "impares" tiene una flecha izquierda, mientras que el pseudo-elemento de cada una de las divs "pares" se muestra como una flecha derecha.
4. Agregando Interactividad
Ahora que la estructura básica de la línea de tiempo está lista, vamos a averiguar los nuevos requisitos:
- De forma predeterminada, los elementos de línea de tiempo (divs) deben estar ocultos.
- Deberán aparecer cuando su padre (elemento de lista) entra en la ventana gráfica.
La primera tarea es relativamente sencilla. El segundo, sin embargo, es un poco más complicada. Tenemos que detectar si los elementos de destino (elementos de lista) son totalmente visibles en la ventana de visualización actual, entonces si eso ocurre revelamos a su hijo. Para implementar esta funcionalidad, no utilizaremos ninguna biblioteca de JavaScript externa (por ejemplo, WOW.js o ScrollReveal.js) o escribir nuestro propio complejo código. Felizmente, hay un hilo popular en StackOverflow sobre este problema. Así que primero vamos a aprovechar la respuesta propuesta para probar si un elemento es visible en la ventana actual o no.
Esta es la función simplificada que vamos a usar:
function isElementInViewport(el) { var rect = el.getBoundingClientRect(); return ( rect.top >= 0 && rect.left >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && rect.right <= (window.innerWidth || document.documentElement.clientWidth) ); }
Agregar clase cuando está en la vista
A continuación, agregamos la clase in-view
a los elementos de la lista que son visibles en la ventana gráfica actual.
Nota: es importante que probemos si son visibles en los siguientes casos:
- Cuando la página se carga
- Mientras nos desplazamos hacia abajo
Si es necesario, podemos hacer algunas pruebas adicionales (como cuando cambia el tamaño de la ventana del navegador).
En nuestro ejemplo, aquí está el código que usamos:
var items = document.querySelectorAll(".timeline li"); // code for the isElementInViewport function function callbackFunc() { for (var i = 0; i < items.length; i++) { if (isElementInViewport(items[i])) { items[i].classList.add("in-view"); } } } window.addEventListener("load", callbackFunc); window.addEventListener("scroll", callbackFunc);
Ahora que hemos añadido nuestro JavaScript, si volvemos a cargar la página, deberíamos ver un resultado similar al siguiente:



Ocultar y Revelar
Vamos ahora a revisar nuestro requisito inicial. Recuerde que, por defecto, todas las divs deben estar ocultas. Para lograr esto, usamos las propiedades de visibility
y opacity
de CSS. Además, usamos el translate3d()
para moverlos 200px lejos de su posición original. Siempre y cuando su padre esté a la vista, los revelaremos y eliminaremos el desplazamiento predefinido. De esta manera, creamos una diapositiva agradable con efectos.
Por
último, otra pequeña cosa que haremos cuando un li
está dentro de la
ventana de visualización, es cambiar el color de fondo de su pseudo-elemento ::before
.
Los siguientes estilos cuidan de todo eso:
.timeline ul li::after { background: #fff; transition: background .5s ease-in-out; } .timeline ul li.in-view::after { background: #F45B69; } .timeline ul li div { visibility: hidden; opacity: 0; transition: all .5s ease-in-out; } .timeline ul li:nth-child(odd) div { transform: translate3d(200px,0,0); } .timeline ul li:nth-child(even) div { transform: translate3d(-200px,0,0); } .timeline ul li.in-view div { transform: none; visibility: visible; opacity: 1; }
La siguiente visualización muestra el estado inicial de nuestra línea de tiempo. Aquí puedes ver los elementos de la línea de tiempo porque les he dado un toque de opacidad sólo para ilustrar dónde están ubicados inicialmente:



Y aquí está el estado final de la línea de tiempo:



5. Hacerlo adaptable a pantallas
¡Estamos casi listos! Lo último que tenemos que hacer es hacer que nuestra línea de tiempo adaptable a pantallas.
Primero, en lo que nos referiremos como "medium screens" (> 600px y ≤900px), solo haremos una pequeña modificación. Específicamente, reducimos el ancho de las divs.
Estas son las reglas que tenemos que cambiar:
@media screen and (max-width: 900px) { .timeline ul li div { width: 250px; } .timeline ul li:nth-child(even) div { left: -289px; /*250+45-6*/ } }
En tal caso, la línea de tiempo se ve como esto:



Sin embargo, en pantallas pequeñas (≤600px), todos los elementos de la línea de tiempo se ven iguales; No hay diferencias entre las divs "impares" y "pares". Una vez más, tenemos que sobrescribir algunas reglas CSS:
@media screen and (max-width: 600px) { .timeline ul li { margin-left: 20px; } .timeline ul li div { width: calc(100vw - 91px); } .timeline ul li:nth-child(even) div { left: 45px; } .timeline ul li:nth-child(even) div::before { left: -15px; border-width: 8px 16px 8px 0; border-color: transparent #F45B69 transparent transparent; } }
En pantallas más pequeñas la línea de tiempo se ve asi:



Nota: en pantallas pequeñas usamos la unidad vw
para especificar el ancho de los elementos de línea de tiempo. No hay razones especiales detrás de este enfoque. Podríamos igualmente haber utilizado porcentajes o píxeles.
Soporte del navegador
La demostración funciona bien en los navegadores y dispositivos más recientes. En los dispositivos iOS, sin embargo, los elementos de la línea de tiempo siempre están visibles, en lugar de aparecer cuando su padre entra en la ventana gráfica.
Desde
mis pruebas he visto que en esos dispositivos las propiedades
window.innerHeight
y document.documentElement.clientHeight
no devuelven
la altura de la ventana real. Específicamente, devuelven un número mucho mayor. Como resultado de esta incoherencia, todos los elementos de lista reciben la clase in-view
cuando se carga la página.
Aunque no es un gran problema (puede que desee las animaciones sólo en pantallas grandes), si sabe más sobre este tema o lo ha visto antes, no olvide dejar detalles en los comentarios.
Conclusion
En este tutorial, creamos una línea de tiempo vertical adaptable a pantallas. Hemos cubierto muchas cosas, así que vamos a recapitular:
- Usando una simple lista desordenada y pseudo-elementos CSS, logramos construir la estructura principal de nuestra línea de tiempo. Una desventaja de este enfoque es que, como ya he mencionado en otro artículo, pseudo-elementos CSS no son 100% accesibles, así que tenga esto en cuenta.
- Aprovechamos un fragmento de código extraído de un hilo popular de StackOverflow para probar si los elementos de la lista están en la vista o no. Luego, escribimos nuestro propio CSS para animar sus elementos secundarios. Alternativamente, podríamos haber usado una biblioteca de JavaScript o escrito nuestro propio código.
Espero que hayas disfrutado del tutorial y usarás esta línea de tiempo como base para construir algo interesante. Si usted tiene alguna pregunta, ¡hágamelo saber en los comentarios de abajo!