How to create a simple, intuitive vanilla JS carousel
Carousels are one of those things in web development that can be made to be very complicated. If you look for tutorials out there, there are many great ones, but a lot of them jump to complex animations, extra features, etc. But the simpler ones only flash between divs/images, instead of sliding through multiple elements on a screen.
In this tutorial, I will walk you through an intuitive understanding of Javascript carousels.
Video Guide:
This is a beginner tutorial, aimed to make an intutive JS carousel, it is not necessarily the most correct or "clever".
Step 1: Starter code
Here is some starter code, with very basic HTML and CSS to get us started.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Carousel</title>
</head>
<style>
body{
font-family:sans-serif
}
#container{
background:#eee;
height:150px;
width:500px;
margin:auto;
display:flex;
justify-content:space-between;
align-items: center;
}
#carousel-track{
width:250px;
height:64px;
}
.carousel-item{
height:64px;
width:64px;
background:#777;
color:#FFF;
}
</style>
<body>
<div id="container">
<button><</button>
<div id="carousel-track">
<div class="carousel-item">A</div>
<div class="carousel-item">B</div>
<div class="carousel-item">C</div>
<div class="carousel-item">D</div>
<div class="carousel-item">E</div>
<div class="carousel-item">F</div>
</div>
<button >></button>
</div>
</body>
<script>
</script>
</html>
You should see something like this:
Step 2: Style the carousel into a row
The first thing we want to do is style the css further, and get all of our boxes into 1 row (we will call this row the #carousel-track.
We will add the following classes to #carousel-track
#carousel-track{
border:1px solid red; /* Shows us what we are working with */
width:250px;
height:64px;
white-space: nowrap; /* Prevents wrapping */
overflow:hidden;
font-size:0px;
}
The weird ``font-size: 0px;`` is to remove unwanted spacing introduced by blank HTML interacting with the inline-block elements that we will make as children to .carousel-item
Ok, we removed the overflow, now add inline-block, recreate the font size removed in the parent, and add a small gap between the items;
.carousel-item{
height:64px;
width:64px;
background:#333;
display:inline-block;
color:#FFF;
font-size:16px;
margin-right:6px;
}
Great! Now our carousel is actually in a row, and the overflow of the carousel is bounded by our track window!
Step 3: Javascript Functionality
Lets add onclick calls to our main function to our two buttons:
<div id="container">
<button onclick="moveCarousel('backward')"><</button>
<div id="carousel-track">
<div class="carousel-item">A</div>
<div class="carousel-item">B</div>
<div class="carousel-item">C</div>
<div class="carousel-item">D</div>
<div class="carousel-item">E</div>
<div class="carousel-item">F</div>
</div>
<button onclick="moveCarousel('forward')">></button>
</div>
Clearly, we are calling one function, with two parameters to move us forward or backward.
In our Javascript, let's create this function and add logic to at least just move something forward:
Had we added
.carousel-item{
transform: translateX(20px)
}
to carousel item, we would've moved all of our items to the right (+x) by 20 pixels. We want to move the whole class, because we want everyone to move, not just a single element.
However, to move "forward" we have to pull everything to the left, to expose elements on the right. So, forward motion should be translated -x pixels.
To add this to our function, let's move all of our elements forward -x, on each click:
function moveCarousel(direction) {
const offset = 68;
document.querySelectorAll('.carousel-item').forEach(carouselItemElement =>{
carouselItemElement.style.transform = `translateX(-${offset}px)`;
})
}
If you want to see the animation better, add a transition to the elements:
.carousel-item{
height:64px;
width:64px;
background:#333;
display:inline-block;
color:#FFF;
font-size:16px;
margin-right:6px;
transition: transform 0.3s ease;
}
Notice that this only works on the first click? Because our elements have been moved TO the -68px position, and every other click, they are already there. So we are not adding to the position, we are just setting it.
So let's "add to our position", by multiplying the -68 by the number of times we have clicked the button, and increase the position everytime we click it.
var position = 0;
function moveCarousel(direction) {
position++;
const offset = 68*position;
document.querySelectorAll('.carousel-item').forEach(carouselItemElement =>{
carouselItemElement.style.transform = `translateX(-${offset}px)`;
})
}
Perfect, now let's move our position backward for the the other button.
In order to do this, we need to decrease our offset, therefore, just decrease our "desired" position
var position = 0;
function moveCarousel(direction) {
if(direction=='forward'){
position++;
}
else if(direction=='backward'){
position--;
}
const offset = 68*position;
document.querySelectorAll('.carousel-item').forEach(carouselItemElement =>{
carouselItemElement.style.transform = `translateX(-${offset}px)`;
})
}
Finally, let's add a gaurd and prevent the function running forward if it's over the boundry, or backward, if it's less than 0.
var position = 0;
function moveCarousel(direction) {
if(direction=='forward'){
if(position==3){
return false;
}
position++;
}
else if(direction=='backward'){
if(position==0){
return false;
}
position--;
}
const offset = 68 * position;
document.querySelectorAll('.carousel-item').forEach(carouselItemElement =>{
carouselItemElement.style.transform = `translateX(-${offset}px)`;
})
}
That's it! You're done! From here, you can expand on it as you wish.
Full Code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Carousel</title>
</head>
<style>
body{
font-family:sans-serif
}
#container{
background:#eee;
height:150px;
width:500px;
margin:auto;
display:flex;
justify-content:space-between;
align-items: center;
}
#carousel-track{
border:1px solid red;
width:250px;
height:64px;
white-space: nowrap; /* Prevents wrapping */
overflow:hidden;
font-size:0px;
}
.carousel-item{
height:64px;
width:64px;
background:#333;
display:inline-block;
color:#FFF;
font-size:16px;
margin-right:6px;
transition: transform 0.3s ease;
}
</style>
<body>
<div id="container">
<button onclick="moveCarousel('backward')"><</button>
<div id="carousel-track">
<div class="carousel-item">A</div>
<div class="carousel-item">B</div>
<div class="carousel-item">C</div>
<div class="carousel-item">D</div>
<div class="carousel-item">E</div>
<div class="carousel-item">F</div>
</div>
<button onclick="moveCarousel('forward')">></button>
</div>
</body>
<script>
var position = 0;
function moveCarousel(direction) {
if(direction=='forward'){
if(position==3){
return false;
}
position++;
}
else if(direction=='backward'){
if(position==0){
return false;
}
position--;
}
const offset = 68 * position;
document.querySelectorAll('.carousel-item').forEach(carouselItemElement =>{
carouselItemElement.style.transform = `translateX(-${offset}px)`;
})
}
</script>
</html>