A very simple slideshow for HTML content using vanilla JavaScript and CSS transitions. This is designed for only one slide to be displayed at a time and is not (currently) a carousel. It supports previous and next buttons, as well as “dots” which are buttons that let you jump to a specific slide.
I was going to call it “Slideshow Bob” but a quick Google search reminded me that I have no original thoughts.
JavaScript
// ***********************************
// Slideshow - Fade In / Out
// ***********************************
const slideshow = () => {
const slideshows = document.querySelectorAll('.slideshow')
slideshows.forEach((slideshow) => {
// get all slide elements
const slides = slideshow.querySelectorAll('.slideshow__slide')
const slideshowControls = slideshow.querySelector('.slideshow__controls')
const slideshowDots = slideshow.querySelector('.slideshow__dots')
// set the interval between slides
const interval = slideshow.getAttribute('data-interval')
// total amount of slides
const slideCount = slides.length
// initial index
let currentSlideIndex = 0
// To store the timeout ID and clear it when needed
let slideTimeout
// recursive function that allows us to loop through the slides
// initially we set the slideIndex to 0 to display the first slide
// then we add 1 to the current slideIndex and use the modulo operator to get the remainder
// this determines the next slide to display and loops back to the first slide when the last slide is reached
const showSlide = (slideIndex) => {
// remove status from all slides
slides.forEach((slide) => {
slide.removeAttribute('data-status')
})
// set status to active on the current slide
slides[slideIndex].setAttribute('data-status', 'active')
clearTimeout(slideTimeout)
slideTimeout = setTimeout(() => {
// go to the next slide and loop back to the first slide when the last slide is reached
let newSlideIndex = (slideIndex + 1) % slideCount
showSlide(newSlideIndex)
// set dot status
setDotStatus()
}, interval)
// used by the prev/next buttons
currentSlideIndex = slideIndex
// console.log(slideIndex)
}
// ***********************************
// Controls - Prev / Next
// ***********************************
const showControls = () => {
if (!slideshowControls) return
const controlsPrev = slideshowControls.querySelector('.slideshow__prev')
const controlsNext = slideshowControls.querySelector('.slideshow__next')
// Prev button click event
controlsPrev.addEventListener('click', () => {
// if the current slide is the first slide, then we want to go to the very last slide
if (currentSlideIndex === 0) {
currentSlideIndex = slideCount
}
// otherwise we want to go to the previous slide
let newSlideIndex = (currentSlideIndex - 1) % slideCount
showSlide(newSlideIndex)
// set dot status
setDotStatus()
})
// Next button click event
controlsNext.addEventListener('click', () => {
// go to the next slide
// but if the current slide is the last slide, then we want to go to the very first slide
let newSlideIndex = (currentSlideIndex + 1) % slideCount
showSlide(newSlideIndex)
// set dot status
setDotStatus()
})
}
showControls()
// ***********************************
// Dots - Jump to Slide Buttons
// ***********************************
showDots = () => {
if (!slideshowDots) return
// create dots for each slide
slides.forEach((slide, index) => {
const dot = document.createElement('button')
dot.classList.add('slideshow__dot')
dot.setAttribute('data-slide', index)
slideshowDots.appendChild(dot)
// initially set the first dot to active
if (index === 0) {
dot.setAttribute('data-status', 'active')
}
})
// get all dot buttons after they have been created dynamically
const slideshowDotButtons = slideshowDots.querySelectorAll('.slideshow__dot')
// dot click event
slideshowDotButtons.forEach((dot) => {
dot.addEventListener('click', () => {
showSlide(dot.getAttribute('data-slide'))
// clear status from all dots
slideshowDotButtons.forEach((allDots) => {
allDots.removeAttribute('data-status')
})
dot.setAttribute('data-status', 'active')
})
})
}
showDots()
// for setting dot status when prev/next buttons are clicked and during the setInterval
const setDotStatus = () => {
if (!slideshowDots) return
const slideshowDotButtons = slideshowDots.querySelectorAll('.slideshow__dot')
slideshowDotButtons.forEach((dot) => {
dot.removeAttribute('data-status')
if (currentSlideIndex === parseInt(dot.getAttribute('data-slide'))) {
dot.setAttribute('data-status', 'active')
}
})
}
// ***********************************
// Start the Slideshow
// ***********************************
showSlide(currentSlideIndex)
})
}
slideshow()
HTML
<div class="slideshow" data-interval="4000">
<div class="slideshow__slide" data-status="active">
<img src="https://images.unsplash.com/photo-1520991729741-f2798d378fc4?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1738&q=80" alt="Slide 1">
</div>
<div class="slideshow__slide">
<img src="https://images.unsplash.com/photo-1576124986805-a22724b69ffb?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1740&q=80" alt="Slide 2">
</div>
<div class="slideshow__slide">
<img src="https://images.unsplash.com/photo-1473609155674-e11c964feac8?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1740&q=80" alt="Slide 3">
</div>
<div class="slideshow__controls">
<button class="slideshow__prev">Prev</button>
<button class="slideshow__next">Next</button>
</div>
<div class="slideshow__dots"></div>
</div>
CSS
// ***********************************
// Slideshow - Fade In / Out
// ***********************************
.slideshow {
display: grid;
place-items: start;
}
// slides
.slideshow__slide {
opacity: 0;
z-index: -1;
grid-column-start: 1;
grid-column-end: 2;
grid-row-start: 1;
grid-row-end: 2;
transition: all 0.5s;
img {
width: 800px;
}
}
.slideshow__slide[data-status='active'] {
opacity: 1;
z-index: 1;
}
// controls (prev / next)
.slideshow__controls {
}
.slideshow__prev {
}
.slideshow__next {
}
// dots
.slideshow__dots {
}
.slideshow__dot {
width: 1em;
height: 1em;
}
.slideshow__dot[data-status='active'] {
background-color: red;
}