DEV Community

Cover image for Slider Component - JavaScript & CSS
boibolang
boibolang

Posted on

Slider Component - JavaScript & CSS

Pada kesempatan kali ini kita akan praktik membuat slider, slider bisa berisi image maupun text nantinya disesuaikan dengan kebutuhan. Idenya adalah dengan menyiapkan image maupun text yang akan dijadikan slide, kemudian terdapat button sebagai navigasi kiri-kanan, nantinya kita juga bisa menggunakan arrow keyboard maupun dot. Untuk praktik kali ini kita akan memakai image sebagai content dari slide. Langsung saja kita siapkan file index.html, style.css dan app.js. Untuk file app.js kita akan buat secara bertahap

<!-- index.html -->

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="style.css" />
    <title>Slider</title>
  </head>
  <body>
    <div class="slider">
      <div class="slide"><img src="../img/img-1.jpg" alt="Photo 1" /></div>
      <div class="slide"><img src="../img/img-2.jpg" alt="Photo 2" /></div>
      <div class="slide"><img src="../img/img-3.jpg" alt="Photo 3" /></div>
      <div class="slide"><img src="../img/img-4.jpg" alt="Photo 4" /></div>
      <button class="slider__btn slider__btn--left">&larr;</button>
      <button class="slider__btn slider__btn--right">&rarr;</button>
      <div class="dots"></div>
    </div>
    <script src="app.js"></script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode
/* style.css */

.slider {
  max-width: 100rem;
  height: 50rem;
  margin: 0 auto;
  position: relative;
  overflow: hidden;
}
.slide {
  position: absolute;
  top: 0;
  width: 100%;
  height: 50rem;

  display: flex;
  align-items: center;
  justify-content: center;

  /* untuk animasi */
  transition: transform 1s;
}

.slide > img {
  /* untuk menyamakan ukuran image */
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.slider__btn {
  position: absolute;
  top: 50%;
  z-index: 10;

  border: none;
  background: rgba(255, 255, 255, 0.7);
  font-family: inherit;
  color: #333;
  border-radius: 50%;
  height: 5.5rem;
  width: 5.5rem;
  font-size: 3.25rem;
  cursor: pointer;
}

.slider__btn--left {
  left: 6%;
  transform: translate(-50%, -50%);
}

.slider__btn--right {
  right: 6%;
  transform: translate(50%, -50%);
}

.dots {
  position: absolute;
  bottom: 5%;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
}

.dots__dot {
  border: none;
  background-color: #b9b9b9;
  opacity: 0.7;
  height: 1rem;
  width: 1rem;
  border-radius: 50%;
  margin-right: 1.75rem;
  cursor: pointer;
  transition: all 0.5s;
}

.dots__dot:last-child {
  margin: 0;
}

.dots__dot--active {
  background-color: red;
  opacity: 1;
}
Enter fullscreen mode Exit fullscreen mode

Untuk file app.js kita akan buat bertahap sebagai berikut :

Posisikan image pada window view (viewport width) berdampingan, dan geser ketika ada trigger

const slides = document.querySelectorAll('.slide');

slides.forEach((s, i) => (s.style.transform = `translateX(${100 * i}%)`));
Enter fullscreen mode Exit fullscreen mode

Dengan menggunakan index dari forEach dan translateX kita bisa menentukan posisi viewport setiap kali ada trigger

image

Supaya image tampak semua kita kecilkan ukurannya, nantinya kita kembalikan ke ukuran normal jika aplikasi sudah jadi

const slides = document.querySelectorAll('.slide');

const slider = document.querySelector('.slider');
slider.style.transform = 'scale(0.2)';
slider.style.overflow = 'visible';

slides.forEach((s, i) => (s.style.transform = `translateX(${100 * i}%)`));
Enter fullscreen mode Exit fullscreen mode

Hasilnya sebagai berikut

image

Assign event handler kepada button


// Component initiation
const slides = document.querySelectorAll('.slide');
const btnLeft = document.querySelector('.slider__btn--left');
const btnRight = document.querySelector('.slider__btn--right');

let curSlide = 0;

const slider = document.querySelector('.slider');
slider.style.transform = 'scale(0.2)';
slider.style.overflow = 'visible';

slides.forEach((s, i) => (s.style.transform = `translateX(${100 * i}%)`));

btnRight.addEventListener('click', function () {
  curSlide++;
  slides.forEach((s, i) => (s.style.transform = `translateX(${100 * (i - curSlide)}%)`));
});

Enter fullscreen mode Exit fullscreen mode

image

Kita perlu memberi limiter supaya slider tidak bergeser terus setelah image-nya habis. Ketika sudah sampai pada image terakhir kita kembalikan ke image awal. Dengan sedikit refactoring kita bisa menyelesaikan kode untuk app.js sebagai berikut

const slides = document.querySelectorAll('.slide');
const btnLeft = document.querySelector('.slider__btn--left');
const btnRight = document.querySelector('.slider__btn--right');

let curSlide = 0;
const maxSlide = slides.length;

const goToSlide = function (slide) {
  slides.forEach((s, i) => (s.style.transform = `translateX(${100 * (i - slide)}%)`));
};

// Activate on first load
goToSlide(0);

// Next slide
const nextSlide = function () {
  if (curSlide === maxSlide - 1) {
    curSlide = 0;
  } else {
    curSlide++;
  }
  goToSlide(curSlide);
};

// Prev slide
const prevSlide = function () {
  if (curSlide === 0) {
    curSlide = maxSlide - 1;
  } else {
    curSlide--;
  }
  goToSlide(curSlide);
};

btnRight.addEventListener('click', nextSlide);
btnLeft.addEventListener('click', prevSlide);
Enter fullscreen mode Exit fullscreen mode

image

Selanjutnya kita tambahkan event handler untuk keyboard arrow left dan right

...
// Event handler for keyboard arrow
document.addEventListener('keydown', function (e) {
  if (e.key === 'ArrowLeft') prevSlide();
  e.key === 'ArrowRight' && nextSlide();
});
Enter fullscreen mode Exit fullscreen mode

Selanjutnya kita tambahkan dot navigation. Kode untuk dot navigation kita tempatkan di awal setelah inisiasi component

const dotContainer = document.querySelector('.dots');

// Dot handler
const createDots = function () {
  slides.forEach((_, i) => {
    dotContainer.insertAdjacentHTML(
      'beforeend',
      `<button class="dots__dot" data-slide="${i}"></button>`
    );
  });
};

// Activate on first load
createDots();

// Event handler for dots
dotContainer.addEventListener('click', function (e) {
  if (e.target.classList.contains('dots__dot')) {
    const { slide } = e.target.dataset;
    goToSlide(slide);
  }
});
Enter fullscreen mode Exit fullscreen mode

image

Tahap akhir kita akan tambahkan active navigation, jadi image yang tampak di window juga akan memberi display aktif pada dot navigation. Dalam hal ini dot navigation akan kita beri warna merah

// Activate dots
const activateDot = function (slide) {
  // Remove class from all
  document
    .querySelectorAll('.dots__dot')
    .forEach((dot) => dot.classList.remove('dots__dot--active'));

  // Activate class only for selected
  document
    .querySelector(`.dots__dot[data-slide="${slide}"]`)
    .classList.add('dots__dot--active');
};
// Activate on first load
activateDot(0);
Enter fullscreen mode Exit fullscreen mode

Kita juga harus menambahkan function activateDot() pada setiap posisi image. Kode lengkap dari app.js adalah sebagai berikut

const slides = document.querySelectorAll('.slide');
const btnLeft = document.querySelector('.slider__btn--left');
const btnRight = document.querySelector('.slider__btn--right');
const dotContainer = document.querySelector('.dots');

let curSlide = 0;
const maxSlide = slides.length;

// Dot handler
const createDots = function () {
  slides.forEach((_, i) => {
    dotContainer.insertAdjacentHTML(
      'beforeend',
      `<button class="dots__dot" data-slide="${i}"></button>`
    );
  });
};

// Activate on first load
createDots();

// Activate dots
const activateDot = function (slide) {
  // Remove class from all
  document
    .querySelectorAll('.dots__dot')
    .forEach((dot) => dot.classList.remove('dots__dot--active'));

  // Activate class only for selected
  document
    .querySelector(`.dots__dot[data-slide="${slide}"]`)
    .classList.add('dots__dot--active');
};
// Activate on first load
activateDot(0);

const goToSlide = function (slide) {
  slides.forEach(
    (s, i) => (s.style.transform = `translateX(${100 * (i - slide)}%)`)
  );
};

goToSlide(0);
activateDot(0);

// Next slide
const nextSlide = function () {
  if (curSlide === maxSlide - 1) {
    curSlide = 0;
  } else {
    curSlide++;
  }
  goToSlide(curSlide);
  activateDot(curSlide);
};

const prevSlide = function () {
  if (curSlide === 0) {
    curSlide = maxSlide - 1;
  } else {
    curSlide--;
  }
  goToSlide(curSlide);
  activateDot(curSlide);
};

btnRight.addEventListener('click', nextSlide);
btnLeft.addEventListener('click', prevSlide);

// Event handler for keyboard arrow
document.addEventListener('keydown', function (e) {
  if (e.key === 'ArrowLeft') prevSlide();
  e.key === 'ArrowRight' && nextSlide();
});

// Event handler for dots
dotContainer.addEventListener('click', function (e) {
  if (e.target.classList.contains('dots__dot')) {
    const { slide } = e.target.dataset;
    goToSlide(slide);
    activateDot(slide);
  }
});

Enter fullscreen mode Exit fullscreen mode

Hasilnya sebagai berikut

image

Top comments (0)