Content Slideshow

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;
}