Crear sliders con JavaScript y Swiper

Para crear sliders con JavaScript, tu mejor opción es Swiper, la librería JavaScript por excelencia para la creación de sliders y carousels.

En la parte final de este post he detallado un ejercicio donde te explico como implementar Swiper para crear un carousel de productos, y un pase de “historias” parecido al de Instagram. Puedes hacer click en la siguiente imagen para ver el resultado final en una ventana nueva.

Ejercicio terminado para generar Sliders con Swiper. Haz click en la imágen para abrilo en una ventana nueva
Ejercicio terminado para generar Sliders con Swiper. Pincha en la imagen para abrilo en una ventana nueva

Si no quieres leer la introducción, y prefieres ir directamente a la parte práctica, haz click aquí

Cuando ya llevas un tiempo creando páginas y aplicaciones web, es inevitable enfrentarse a requerimientos que se repiten una y otra vez.

En mi caso, ya he perdido la cuenta de las veces que he tenido que implementar un slider (pase de imágenes, carousel o desplazador) en proyectos en los que he trabajado.

Por eso hace tiempo que no me rompo la cabeza buscando cuál es la mejor opción para crear elementos de este tipo.

En mi opinión la librería Swiper es la clara ganadora, y en esta publicación te voy a contar porqué.

Swiper la librería JavaScript para crear sliders.

Ya sea en apps o en páginas corporativas, los sliders son elementos UI útiles y ampliamente solicitados por los clientes.

No es de extrañar, mediante un deslizador, se pueden mostrar contenidos de todo tipo de forma dinámica y atractiva.

Desde simples imágenes promocionales, hasta «cards» con productos destacados

Es por eso, que sea habitual ver tantos sitios web con un carrusel situado al inicio de la página, justo por debajo del menú principal.

En este punto, puede que pienses, genial, solo llegas unos cuantos años tarde…, la implementación de sliders lleva décadas superada. Una rápida búsqueda arroja miles de resultados que resuelven esta necesidad.

Estoy de acuerdo contigo, pero permíteme que te adelante una importante observación.

El interés de este post no radica en qué ofrece Swiper, sinó en por qué es la mejor de entre todas las opciones que existen.

Pronto descubrirás que su uso ofrece una experiencia de desarrollo única y versátil, con unos resultados muy profesionales.

Evidentemente, podrías no estar de acuerdo conmigo, si es así, me gustaría que dejaras un comentario al final del post para conocer tu opinión.

Antes de decantarme por SwiperJs estuve recopilando y estudiando muchas de las alternativas que existen.

Descarté todas esas que tienen jQuery como dependencia, porque ya sabes, estamos a las puertas de 2024, y el lenguaje JavaScript, así como los navegadores han evolucionado mucho.

No malinterpretes mis palabras, jQuery es una herramienta que merece todo mi respeto.

Tras esa criba, ya quedaron bastantes menos competidores de Swiper.

Acto seguido estudié sus APIs y analicé varios de sus ejemplos.

En mi opinión Swiper ganó por goleada, no solo en cantidad de opciones sino también en capacidad de adaptación a distintos frameworks.

Tal y como veremos más adelante, da soporte a frameworks como React, Angular, Vue e incluso Web components. Pero donde realmente brilla Swiper es en la implementación de eventos táctiles.

Crea sliders con JavaScript y Swiper
Crea sliders con JavaScript y Swiper

En un dispositivo móvil se siente como un componente de aplicación nativa, ofreciendo una experiencia de usuario excelente.

SwiperJs nació de la mano de Vladimir Kharlampidi hace más de 6 años.

Por cierto, no es la primera librería de este desarrollador que analizamos por aquí. 

Hace un tiempo ya vimos Atropos, una biblioteca JS creada por el mismo Kharlampidi, para la creación de efectos parallax.

Crear efectos parallax con Atropos

Incluso después de tanto tiempo, Swiper sigue activamente mantenida por un equipo de 280 personas, en un repositorio valorado con más de 36.7K estrellas.

La última semana se ha descargado casi dos millones de veces desde npmjs.com. Una cifra nada desdeñable, verdad?.

Estos datos ya deberían darme la razón, pero es que además, recientemente descubrí algo muy interesante sobre Swiper.

Resulta que Ionic, la famosa librería de componentes para el desarrollo de apps híbridas, desechó su propio componente slider en favor de Swiper.

Así es, en la documentación oficial de Ionic se puede ver cómo animan a los programadores a incluir SwiperJs en caso de necesitar un buen slider.

https://ionicframework.com/docs/vue/slides

Por cierto, si no conoces Ionic o Capacitor, no puedes perderte el post donde hablé de estas tecnologías.

Crear apps híbridas con CapacitorJs

Pero basta de preámbulos, vamos a conocer cómo implementar Swiper en un proyecto creado desde cero.

API y características de SwiperJs

La herramienta se puede instalar a través del comando:

npm install swiper

Una vez incluida en el directorio node_modules, Swiper se puede instanciar de distintas formas, según el framework que desees usar.

En esta publicación analizaré su uso con “Vanilla Js” y «web components», debes tener en cuenta que todo lo que aprendas se puede extrapolar a entornos como React, Vue, Angular.

Antes de programar el script que haga uso de la librería, es necesario preparar algunos elementos HTML.

Un ejemplo de estos, sería el siguiente:

<div class="swiper">
  <div class="swiper-wrapper">
    <div class="swiper-slide">Slide 1</div>
    <div class="swiper-slide">Slide 2</div>
    <div class="swiper-slide">Slide 3</div>
  </div>
  <div class="swiper-pagination"></div>
  <div class="swiper-button-prev"></div>
  <div class="swiper-button-next"></div>
</div>

Presta especial atención a las clases CSS asignadas a cada etiqueta, más adelante serán necesarias para activar ciertas funciones en nuestro slider.

  • swiper: El contenedor con ésta clase actúa de nodo principal del slider, es decir lo usaremos para indicar a Swiper que allí debe configurar un carousel.
  • swiper-wrapper: El nombre de esta clase es obligatorio ya que internamente SwiperJs lo identifica como el elemento padre de los múltiples «slides» o tarjetas desplazables.
  • swiper-slide: Tal como su nombre indica se trata de cada «slide» individual.

Este listado conforma los elementos mínimos necesarios para cualquier slider creado con Swiper.

Sin embargo, podemos agregar más funcionalidades con los siguientes elementos y sus clases CSS.

  • swiper-pagination: Un contenedor donde se muestran «bullets» una forma de paginado propio de este tipo de componentes.
  • swiper-button-prev y swiper-button-next: Swiper se encargará de incluir flechas para proveer al usuario de una navegación adicional.

En conjunto, todo este código HTML actúa de estructura base para instanciar correctamente el slider en nuestro archivo JavaScript.

Su API es similar a la de muchos otros recursos JavaScript, basta con importar la clase principal e instanciarla pasándole dos parámetros.

import Swiper from 'swiper';
const swiper = new Swiper('.swiper', {
  speed: 400,
  spaceBetween: 100,
});

El primer argumento es un “string” que actúa de selector CSS para el contenedor principal.

Swiper buscará el elemento del DOM que contenga esta clase y lo usará para construir el slider. En el caso del ejemplo anterior se trata de la clase “.swiper”.

El segundo parámetro es un objeto JavaScript literal con una serie de propiedades para configurar el comportamiento del deslizador.

Puedes creerme cuando te digo que Swiper ofrece una cantidad enorme de opciones de configuración.

Voy a detallar los que me parecen más interesantes, pero al final del listado te dejo con un enlace a la documentación completa de este objeto.

  • autoplay: Parámetro para iniciar automáticamente el desplazamiento del slider.
  • breakpoints: Sin duda uno de los más necesarios. Gracias a esta propiedad podrás alterar los demás parámetros según el tamaño de pantalla del usuario, lo cual es clave para una interacción “responsive”.
  • centeredSlides: Variable boleana para indicar a Swiper que desamor centrar los contenedores “slider” de nuestro carrousel.
  • direction: mediante ‘horizontal’ o ‘vertical’ configuramos la dirección del slider.
  • effect: Con “effect” podemos setear el tipo de transición. Los efectos deben importarse como módulos independientes, tal y como veremos más adelante.
  • enabled: Boleano para activar y desactivar la instancia.
  • initialSlide: Pasándole un número a este parámetro, se puede decidir en qué elemento se coloca el deslizador de inicio.
  • keyboard: Como su nombre indica, Swiper también da soporte al control por teclado del carousel.
  • loop: Al setear “loop” como true el usuario podrá desplazar los “slides” sin fin, ya que el sistema quedará cerrado en un bucle.
  • modules: Array para incluir módulos adicionales. En los siguientes párrafos veremos en detalle su funcionamiento.
  • navigation: Ésta propiedad agrega una navegación adicional a través de flechas en los extremos del deslizador.
  • mousewheel: Al igual que keyboard, Swiper también da la posibilidad de de interactuar con el slider mediante la rueda del ratón.
  • pagination: Si tenemos la necesidad de indicar al usuario cuantos elementos contiene el carrusel, se puede añadir un paginado basado en “bullets”.
  • scrollbar: Swiper también incluirá una barra de scroll interna si se lo indicamos con esta propiedad.

En esta url dispones de todos los parámetros disponibles:

https://swiperjs.com/swiper-api#parameters

Debes tener en cuenta que Swiper pone a disposición del desarrollador módulos para extender las funcionalidades básicas.

Sin embargo, con el objetivo de no sobrecargar innecesariamente el peso de la librería, estos módulos vienen separados en archivos independientes que debemos importar según nuestros intereses.

En otras palabras, para generar un paginado no es suficiente añadir un elemento HTML con la clase “.swiper-pagination” y sus parámetros de configuración, además, debemos incluir el módulo correspondiente.

Con un ejemplo lo verás muy claro.

import Swiper from 'swiper';
import { Navigation, Pagination, Scrollbar } from 'swiper/modules';
import 'swiper/scss';
import 'swiper/scss/navigation';
import 'swiper/scss/pagination';
import 'swiper/scss/scrollbar';
const swiper = new Swiper('.swiper', {
  modules: [Navigation, Pagination, Scrollbar],
  speed: 500,
  navigation: {
    nextEl: '.swiper-button-next',
    prevEl: '.swiper-button-prev',
  },
  pagination:{
    clickable: true
  },
  scrollbar:{
    enabled: true
  }
});

Como ves, es necesario importar la clase principal y estilos, así como los distintos módulos y sus porciones de CSS.

Puedes encontrar la información de todos los módulos en la documentación:

https://swiperjs.com/swiper-api#modules

Una vez instanciado el objeto swiper, éste ofrece un conjunto de métodos y propiedades para manipular el slider programáticamente.

De nuevo existen muchos métodos interesantes, y te animo a visitar la documentación para conocerlos todos.

No obstante aquí listo algunos de los más relevantes:

  • swiper.slideTo(index, speed, runCallbacks) Programáticamente puedes transicionar a un slide específico.
  • swiper.slideNext(speed, runCallbacks) Salta al siguiente slide tras llamar a este método.
  • swiper.slidePrev(speed, runCallbacks) De igual modo, retrocede al anterior slide de tu contenido.
  • swiper.on(event, handler) Escucha eventos específicos de Swiper y vincula funciones de retorno a estos.
  • swiper.disable() Desactiva tu carousel con la función disable.
  • swiper.update() Refresca el estado de la variable swiper. Este método es ideal si alteramos la cantidad de sliders dinámicamente, por ejemplo, tras una llamada a una API Rest.

Crear sliders con JavaScript y Swiper

Ha llegado la hora de poner en práctica la librería a través de un ejercicio real.

En concreto vamos a generar una suerte de tienda online ficticia con dos sliders.

Uno representará una serie de productos destacados, el otro simulará un pase de imágenes propio las historias en redes sociales como Instagram.

En ésta ocasión haremos uso de los componentes web que Swiper ofrece con la instalación de la librería.

Como siempre, puedes acudir al repositorio en GitHub para consultar el código fuente según avance el tutorial.

https://github.com/Danivalldo/libreriasjs/tree/master/Swiper

Comenzamos instalando todas las dependencias necesarias a través de NPM.

npm install swiper sass @shoelace-style/shoelace

Adicionalmente a Swiper he incluido Sass como preprocesador CSS y Shoelace para la cómoda construcción de una modal.

Acto seguido preparamos una estructura HTML básica, compuesta de los siguientes elementos.

Un contenedor con la clase «.header» para representar una cabecera.

<div class="header">
  <span class="brand">
    <img src="./imgs/storefront_FILL0_wght200_GRAD-25_opsz48.svg" alt="" />
    Mi tienda <span class="powered-by">by librerias.js</span>
  </span>
  <span class="user-profile-wrapper">
    <img src="https://robohash.org/minimadoloreslaborum.png" alt="" />
  </span>
</div>

Esta cabecera incluye tanto el logo de la tienda como un botón para acceder a las historias del usuario.

Justo a continuación, añadimos una div con la clase «.main-container».  Allí es donde ubicamos el slider de productos destacados.

De hecho, vamos programar directamente con HTML el componente web con algunas opciones configuradas mediante atributos.

También agregamos etiquetas «swiper-slide» con algunos productos en su interior.

<div class="main-container">
  <h3>Productos destacados</h3>
  <swiper-container
    init="false"
    id="productsSlider"
    slides-per-view="1"
    navigation="true"
    pagination="true"
    space-between="20"
    class="products-slider"
    grab-cursor="true"
    autoplay="true"
    autoplay-delay="2000"
  >
    <swiper-slide class="product-card">
      <img class="thumbnail" src="./imgs/furniture_1.png" />
      <div class="product-info">
        <h2 class="title">Elegant White Sofa</h2>
        <p class="description">
          Elevate your living space with our elegant white sofa.
        </p>
        <p class="price">
          <span>200€</span>
          <button class="add-to-cart">+</button>
        </p>
      </div>
    </swiper-slide>
    <swiper-slide class="product-card">
      <!-- Aquí va el contenido de otro producto como el de arriba -->
    </swiper-slide>
    <swiper-slide class="product-card">
      <!-- Aquí va el contenido de otro producto como el de arriba -->
    </swiper-slide>
  </swiper-container>
</div>

Analiza detenidamente el bloque de código anterior. Fíjate cómo he declarado cada opción de configuración mediante atributos.

Seguimos ampliando el layout añadiendo un pié de página y un componente modal.

<div class="footer">LibreriasJs 2023</div>
<sl-dialog class="history-dialog">
  <swiper-container
    class="history-dialog-swiper"
    effect="cube"
    pagination="true"
    autoplay="true"
    loop="true"
    autoplay-delay="1000"
    grab-cursor="true"
  >
    <swiper-slide class="history-dialog-slide">
      <img src="./imgs/furniture_1.png" alt="" />
    </swiper-slide>
    <swiper-slide class="history-dialog-slide">
      <img src="./imgs/furniture_2.png" alt="" />
    </swiper-slide>
    <swiper-slide class="history-dialog-slide">
      <img src="./imgs/furniture_3.png" alt="" />
    </swiper-slide>
    <swiper-slide class="history-dialog-slide">
      <img src="./imgs/furniture_4.png" alt="" />
    </swiper-slide>
  </swiper-container>
</sl-dialog>

Nuevamente puedes observar cómo declaramos otro carrousel con distintas imágenes en su interior.

En el siguiente punto, dotamos de estilos la interfaz gráfica.

Para lograrlo creamos un archivo «style.scss» con las clases que siguen.

@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@100;300;700&display=swap");
* {
  box-sizing: border-box;
}
:root {
  --mainColorValues: 0, 109, 173;
  --secondaryColorValues: 149, 31, 199;
  --mainColor: rgb(var(--mainColorValues));
  /* --mainColor: rgb(0, 109, 173); */
  /* --secondaryColor: rgb(var(--secondaryColorValues)); */
  --secondaryColor: rgb(51, 161, 225);
}
body {
  margin: 0;
  font-family: "Poppins", sans-serif;
  background-color: #f5f5f5;
  text-rendering: geometricPrecision;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}
.main-container {
  flex: 1;
}
h3 {
  text-align: center;
  margin: 0;
  color: var(--mainColor);
  padding-top: 20px;
  padding-bottom: 10px;
}
.header {
  color: #fff;
  padding: 10px 30px;
  font-weight: 700;
  background-color: var(--secondaryColor);
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-bottom: solid 3px var(--mainColor);
  .brand {
    display: flex;
    gap: 10px;
    align-items: flex-end;
  }
  .powered-by {
    font-size: 0.8rem;
    font-weight: 300;
  }
  .user-profile-wrapper {
    background-color: var(--mainColor);
    border-radius: 50px;
    width: 50px;
    height: 50px;
    display: flex;
    justify-content: center;
    overflow: hidden;
    align-items: center;
    padding: 2px;
    position: relative;
    cursor: pointer;
    img {
      display: block;
      width: 100%;
      background-color: var(--mainColor);
      height: 100%;
      position: relative;
      border-radius: 50px;
      z-index: 2;
    }
    &:after {
      content: "";
      display: block;
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      animation-name: animation-gradient;
      animation-duration: 1s;
      animation-iteration-count: infinite;
      /* background-color: red; */
      background: linear-gradient(to right, red, purple);
    }
  }
}
.products-slider {
  max-width: 1500px;
  padding: 20px;
  .product-card {
    /* border: solid 1px red; */
    position: relative;
    height: 400px;
    border-radius: 20px;
    margin-bottom: 20px;
    overflow: hidden;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
    .thumbnail {
      display: block;
      width: 100%;
      height: 100%;
      object-fit: cover;
      object-position: top center;
    }
    .product-info {
      position: absolute;
      bottom: 10px;
      left: 10px;
      width: calc(100% - 20px);
      background-color: rgba(var(--mainColorValues), 0.4);
      color: #fff;
      border-top: solid 1px rgba(255, 255, 255, 0.3);
      padding: 10px;
      backdrop-filter: blur(5px);
      border-radius: 20px;
      z-index: 2;
      h2 {
        font-size: 1em;
        margin: 0;
      }
      .price {
        font-weight: 700;
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 0;
        padding-top: 10px;
        font-size: 1.3rem;
        border-top: solid 1px rgba(255, 255, 255, 0.2);
        .add-to-cart {
          background-color: var(--mainColor);
          color: #fff;
          border: none;
          font-size: 1rem;
          display: flex;
          justify-content: center;
          align-items: center;
          border-radius: 50px;
          line-height: 0px;
          height: 30px;
          width: 30px;
          cursor: pointer;
          &:hover {
            background-color: var(--secondaryColor);
          }
        }
      }
    }
    &:after {
      content: "";
      position: absolute;
      left: 0;
      top: 0;
      width: 100%;
      height: 100%;
      background-color: #000;
      opacity: 0.1;
      pointer-events: none;
    }
  }
  &::part(button-prev),
  &::part(button-next) {
    color: var(--mainColor);
  }
  &::part(bullet-active) {
    background-color: var(--mainColor);
  }
}
.history-dialog {
  &::part(panel) {
    background-color: transparent;
    box-shadow: none;
  }
  &::part(body) {
    overflow: hidden;
  }
  &::part(overlay) {
    background-color: #000;
    opacity: 0.9;
  }
  .history-dialog-swiper .history-dialog-slide img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
}
.footer {
  background-color: var(--mainColor);
  color: #fff;
  padding: 10px;
  font-size: 0.8rem;
  text-align: center;
}
@keyframes animation-gradient {
  0% {
    transform: rotateZ(0deg);
  }
  100% {
    transform: rotateZ(360deg);
  }
}

Puedes dedicar un tiempo a leerlo detenidamente.

Para terminar, creamos un archivo «main.js» donde programar el script principal.

En las primeras líneas de código importaremos todas las dependencias.

import "./style.scss";
import "@shoelace-style/shoelace/dist/themes/light.css";
import { register } from "swiper/element/bundle";
import "@shoelace-style/shoelace/dist/components/dialog/dialog.js";

Seguidamente llamamos a la función «register» encargada de habilitar el uso de los componentes web de Swiper.

register();

Guardamos en variables una serie de referencias al DOM.

const productsSlider = document.querySelector("#productsSlider");
const userProfileBtn = document.querySelector(".user-profile-wrapper");
const dialog = document.querySelector(".history-dialog");
const historiesSlider = document.querySelector(".history-dialog-swiper");

Fíjate cómo entre estas referencias también están los sliders «productsSlider» y «historiesSlider».

En un objeto JavaScript declaramos unos parámetros de configuración adicionales.

Específicamente definiremos los «breakpoints» para cada ancho de pantalla.

Combinamos dicho objeto con los atributos que especificamos a nivel de etiqueta HTML.

const swiperParams = {
  breakpoints: {
    640: {
      slidesPerView: 2,
    },
    1024: {
      slidesPerView: 3,
    },
    1300: {
      slidesPerView: 4,
    },
  },
};
Object.assign(productsSlider, swiperParams);

Inmediatamente después podemos inicializar el slider.

productsSlider.initialize();

También vamos a construir una función que solicita productos de una API Rest y los añade dinámicamente al slider.

Llamamos a esa función «getProducts» y programamos una petición a una API abierta.

const getProducts = async () => {
  const response = await fetch(
    `https://dummyjson.com/products/category/furniture?limit=10`
  );
  const dataProducts = await response.json();
  for (let i = 0, j = dataProducts.products.length; i < j; i++) {
    const product = dataProducts.products[i];
    const slider = document.createElement("swiper-slide");
    const thumb = document.createElement("img");
    thumb.classList.add("thumbnail");
    thumb.src = product.thumbnail;
    const productInfo = document.createElement("div");
    productInfo.classList.add("product-info");
    productInfo.innerHTML = `
      <h2 class="title">${product.title}</h2>
      <p class="description">${product.description}</p>
      <p class="price">
        <span>${product.price}€</span>
        <button class="add-to-cart">+</button>
      </p>
    `;
    slider.appendChild(thumb);
    slider.appendChild(productInfo);
    slider.classList.add("product-card");
    productsSlider.appendChild(slider);
  }
  productsSlider.swiper.update();
};

A través de la API Dom del navegador generamos «cards» nuevas a partir de los datos recibidos.

Finalizamos el programa con una pocas instrucciones más.

Agregamos la función de click sobre la imagen del usuario para mostrar la modal.

userProfileBtn.addEventListener("click", () => {
  dialog.show();
});

Cada vez que la modal se muestra se actualiza el slider de su interior.

dialog.addEventListener("sl-show", () => {
  window.setTimeout(() => {
    historiesSlider.swiper.update();
  }, 100);
});

Ya solo queda escuchar el evento de carga del navegador y llamar a “getProducts”

window.addEventListener("load", getProducts);

Conclusiones y librerías alternativas

Espero haberte convencido de que Swiper es la mejor opción a la hora de generar sliders, carousels u otro tipo de componentes desplazadores web.

Si después de todo no opinas lo mismo, me encantaría que dejaras un comentario explicando tu punto de vista.

En cualquier caso, te dejo un listado de librerías JavaScript alternativas a Swiper, así como otros recursos adicionales.

Menciones de honor para crear sliders además de Swiper:

Recursos adicionales:

Muchas gracias por leer libreriasjs, hasta la próxima.

Deja un comentario