Skip to content

Instantly share code, notes, and snippets.

@DavidColby
Created May 6, 2021 22:12
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save DavidColby/3d31c47fc0345a49f50774540d2231dc to your computer and use it in GitHub Desktop.
Save DavidColby/3d31c47fc0345a49f50774540d2231dc to your computer and use it in GitHub Desktop.
Implementation of a horizontal slider component with Stimulus and Tailwind CSS
<!--
This code is meant to accompany the guide originally published at https://colby.so/posts/building-a-horizontal-slider-with-stimulus-and-tailwind
It intentionally pulls in Tailwind CSS and Stimulus without a build system to simplify the guide. You shouldn't do this with a real application,
you should use a build system like webpack!
-->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Horizontal slider with Stimulus and Tailwind</title>
<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
<script src="https://unpkg.com/stimulus/dist/stimulus.umd.js"></script>
<script>
(() => {
const application = Stimulus.Application.start()
application.register("slider", class extends Stimulus.Controller {
static get targets() {
return [ "scrollContainer", "image", "indicator" ]
}
initialize() {
this.observer = new IntersectionObserver(this.onIntersectionObserved.bind(this), {
root: this.scrollContainerTarget,
threshold: 0.5
})
this.imageTargets.forEach(image => {
this.observer.observe(image)
})
}
onIntersectionObserved(entries) {
entries.forEach(entry => {
if (entry.intersectionRatio > 0.5) {
const intersectingIndex = this.imageTargets.indexOf(entry.target)
this.indicatorTargets[intersectingIndex].classList.add("bg-blue-900")
}
else {
const intersectingIndex = this.imageTargets.indexOf(entry.target)
this.indicatorTargets[intersectingIndex].classList.remove("bg-blue-900")
}
})
}
scrollTo() {
const imageId = event.target.dataset.imageId
const imageElement = document.getElementById(imageId)
const imageCoordinates = imageElement.getBoundingClientRect()
this.scrollContainerTarget.scrollTo({ left: (this.scrollContainerTarget.scrollLeft + imageCoordinates.left), top: false, behavior: "smooth" })
}
})
})()
</script>
<style type="text/css">
.gallery-item {
scroll-snap-align: start;
}
.gallery {
-webkit-overflow-scrolling: touch;
scroll-snap-type: x mandatory;
}
</style>
</head>
<body>
<main>
<div class="flex flex-col my-24" data-controller="slider">
<h1 class="text-3xl text-gray-900 text-center mb-4">Our slider's title</h1>
<div class="flex overflow-x-scroll hide-scroll-bar overscroll-x-contain gallery" data-slider-target="scrollContainer">
<div class="w-96 h-64 px-4 flex-shrink-0 gallery-item" data-slider-target="image" id="1">
<img src="https://images.unsplash.com/photo-1542291026-7eec264c27ff?ixid=MnwxMjA3fDB8MHxzZWFyY2h8MXx8c2hvZXN8ZW58MHx8MHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=900&q=60" />
</div>
<div class="w-96 h-64 px-4 flex-shrink-0 gallery-item" data-slider-target="image" id="2">
<img src="https://images.unsplash.com/photo-1542291026-7eec264c27ff?ixid=MnwxMjA3fDB8MHxzZWFyY2h8MXx8c2hvZXN8ZW58MHx8MHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=900&q=60" />
</div>
<div class="w-96 h-64 px-4 flex-shrink-0 gallery-item" data-slider-target="image" id="3">
<img src="https://images.unsplash.com/photo-1542291026-7eec264c27ff?ixid=MnwxMjA3fDB8MHxzZWFyY2h8MXx8c2hvZXN8ZW58MHx8MHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=900&q=60" />
</div>
<div class="w-96 h-64 px-4 flex-shrink-0 gallery-item" data-slider-target="image" id="4">
<img src="https://images.unsplash.com/photo-1542291026-7eec264c27ff?ixid=MnwxMjA3fDB8MHxzZWFyY2h8MXx8c2hvZXN8ZW58MHx8MHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=900&q=60" />
</div>
<div class="w-96 h-64 px-4 flex-shrink-0 gallery-item" data-slider-target="image" id="5">
<img src="https://images.unsplash.com/photo-1542291026-7eec264c27ff?ixid=MnwxMjA3fDB8MHxzZWFyY2h8MXx8c2hvZXN8ZW58MHx8MHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=900&q=60" />
</div>
<div class="w-96 h-64 px-4 flex-shrink-0 gallery-item" data-slider-target="image" id="6">
<img src="https://images.unsplash.com/photo-1542291026-7eec264c27ff?ixid=MnwxMjA3fDB8MHxzZWFyY2h8MXx8c2hvZXN8ZW58MHx8MHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=900&q=60" />
</div>
</div>
<div class="flex mx-auto my-8">
<ul class="flex justify-center">
<!-- Note that we have one <li> for each image in our gallery -->
<li class="h-6 w-6 rounded-full mx-2 cursor-pointer bg-gray-500" data-slider-target="indicator" data-image-id="1" data-action="click->slider#scrollTo"></li>
<li class="h-6 w-6 rounded-full mx-2 cursor-pointer bg-gray-500" data-slider-target="indicator" data-image-id="2" data-action="click->slider#scrollTo"></li>
<li class="h-6 w-6 rounded-full mx-2 cursor-pointer bg-gray-500" data-slider-target="indicator" data-image-id="3" data-action="click->slider#scrollTo"></li>
<li class="h-6 w-6 rounded-full mx-2 cursor-pointer bg-gray-500" data-slider-target="indicator" data-image-id="4" data-action="click->slider#scrollTo"></li>
<li class="h-6 w-6 rounded-full mx-2 cursor-pointer bg-gray-500" data-slider-target="indicator" data-image-id="5" data-action="click->slider#scrollTo"></li>
<li class="h-6 w-6 rounded-full mx-2 cursor-pointer bg-gray-500" data-slider-target="indicator" data-image-id="6" data-action="click->slider#scrollTo"></li>
</ul>
</div>
</div>
</main>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment