An image slider web application
An image slider is one of the projects that are prescribed practice for new frontend developers. Let’s build one in this article.
Initially, an image will be displayed on our web page, with a “Next”, and a “Previous” button below it. Clicking the “Next” button will make the currently displayed image slide appear to slide away and disappear towards the left, while another image appears to slide from left to right. A similar but opposite effect is expected on the “Previous” button.
The starter HTML
Here’s the HTML structure:
<!DOCTYPE html>
<html>
<head>
<title>Image slider</title>
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="fontawesome/css/all.min.css">
</head>
<body>
<h2>An image slider</h2>
<section class="centered">
<div id="slider"></div>
<div class="btn-bar">
<button id="previous">
<i class="fas fa-backward"></i>
</button>
<button id="next">
<i class="fas fa-forward"></i>
</button>
</div>
</section>
<script src="app.js"></script>
</body>
</html>
In the head
element, we link to our stylesheet which we’ll write in a moment. We link to the fontawesome stylesheet as well. I assume that we’ve downloaded and placed the fontawesome distribution in the project directory.
In the body
element, we have a heading, followed by a section
element, where all the action happens. It has a class of centered
, as that is how we want it to appear. It contains a div
element, with an id
of slider
, in which we will show our images. It also has a div
element with a class
of btn-bar
which holds two button
elements. We’re using fontawesome icons for the “Next” and “Previous” buttons. The button
elements are assigned unique id
s so we can access and associate event handlers to them in JavaScript. Towards the end, we link to our app.js
file in a script
tag.
The images
I downloaded 5 free images from unsplash using the unsplash image downloader Chrome extension so that they are all of equal size. I put them in an imgs
subdirectory in the project directory.
The CSS
Here’s the CSS that we start with:
*, ::before, ::after {
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
background: rgba(0, 0, 0, 0.15);
}
h2 {
text-align: center;
margin: 10px 0;
}
.centered {
margin: 0 auto;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 600px;
}
#slider {
width: 600px;
height: 400px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.8);
display: flex;
}
#slider img {
width: 100%;
transition: transform 1s ease;
}
.centered .btn-bar {
display: flex;
width: 50%;
justify-content: space-evenly;
margin: 10px 0;
}
We start with a CSS reset to get rid of some browser-specific settings for a perdictable layout. We give the page a grayish background color. We center align the h2
heading. We also give it a bit of spacing above and below, so that it isn’t too close to the window’s top edge and sufficiently spaced from the content below it.
We horizontally center our section
element with the margin: 0 auto;
directive. To control the layout of the images and the buttons in separate rows, we choose CSS Flexbox, and set its flex-direction
property to column
. To center the contents of this section
vertically as well as horizontally by setting the align-items
and justify-content
properties to center
. We give the section
a width
of 600 pixels which should be enough for our images. We’ll let the content dictate this element’s height.
When I displayed an image with a width of 600 pixels, its height was 400 pixels. So, on the slider
element, I set a height of 400 pixels and a width of 600 pixels. I gave a box shadow to create a 3D effect. To arrange the images inside this container, I set its display
property to flex
.
Then, I set the img
elements to have a width
equal to 100% of the slider
container. I also applied a transition effect of a one second duration on the images. We’ll set the actual transform later.
To arrange the buttons, I selected the btn-bar
class and picked Flexbox for its layout. I set it to half the width of its container, because having the buttons near the center of the display makes more sense. I configure Flexbox to space the buttons evently within this width, and gave the buttons a bit of margin on the top and bottom to space the buttons away from the images above them.
Displaying the images
We haven’t inserted the images, yet. Let’s do that using JavaScript. We’ll create img
elements for each image, and append it to the slider
element. I named my images pic1.jpg
, pic2.jpg
, pic3.jpg
, pic4.jpg
, and pic5.jpg
.
const numImages = 5
const sliderElement = document.getElementById('slider')
loadImages()
function loadImages() {
for (let i = 1 ; i <= numImages ; i++) {
const imgElement = document.createElement('img')
imgElement.src = `imgs/pic${i}.jpg`
sliderElement.appendChild(imgElement)
}
}
We initialize a variable to the total number of images. We acquire an object representing the slider
element so that we can add the images to it. We run a for
loop 5 times. Each time through the loop, we create an img
element using the document.createElement()
method. We set the src
attribute of the img
element to an image’s path using a template literal string. In the first iteration of the for
loop, the value of i
is 1, so that template literal string equals imgs/pic1.jpg
which is the relative path of the first image. Similarly, all img
elements are created and appended to the slider
element using the appendChild()
method. Since we’ve called the loadImages()
function, this creates the following markup:
<div id="slider">
<img src="imgs/pic1.jpg">
<img src="imgs/pic2.jpg">
<img src="imgs/pic3.jpg">
<img src="imgs/pic4.jpg">
<img src="imgs/pic5.jpg">
</div>
At this point, you should see all five images displayed side by side inside the slider
element.
Of course, that’s not what we want. We want only one image displayed. But we’ll fix that shortly. Right now, it helps to be able to see all the images the way they are. If you just set overflow: hidden;
on the slider
element, you’ll see just one image like this:
But, don’t save that setting just yet. Like I said, it helps to see all the images for the sliding effect.
The sliding effect in JavaScript
Now, let’s implement the sliding effect on the images. First, let’s think what we need to do to slide to the next image. The first image is already displayed at the center. To get the second image at the center, we could shift all of the images to the left. That is a translateX()
transform by an amount of -100%
. Think of a band on which the five images are stuck, and you slid the band to the left. What does it take to display the third image? A translateX(-200%)
on all the images. So on and so forth.
The following image shows what it takes to display a given image number.
With an x translation of 0%, image number 1 is in the center frame. To display image number 2, all images must be x translated by -100%, and so on. In general, to display image number i
, all the images must be x translated by (i-1)*100%
.
It’s implementation time.
let imageNumber = 1
const numImages = 5
const sliderElement = document.getElementById('slider')
const nextButton = document.getElementById('next')
let images
nextButton.addEventListener('click', nextImage)
loadImages()
function loadImages() {
for (let i = 1 ; i <= numImages ; i++) {
const imgElement = document.createElement('img')
imgElement.src = `imgs/pic${i}.jpg`
sliderElement.appendChild(imgElement)
}
images = document.querySelectorAll('#slider img')
}
function showSlide() {
images.forEach( img => {
img.style.transform = `translateX(-${(imageNumber - 1) * 100}%)`
})
}
function nextImage() {
if (imageNumber < numImages) {
imageNumber++
}
else {
imageNumber = 1
}
showSlide()
}
We add a variable to hold the currently displayed image’s number. Another thing that we do is to add a line to the loadImages()
function which obtains an object referencing all the img
elements that are inside the slider
element. This collection is stored in the images
variable.
We acquire objects referencing the “Next” button. We hook up a click event handler on the “Next” button. In that event handler, we increment the current image number as long as its present value is less than 5. If it is already at 5, we reset it to 1 (we loop around to image number 1). Once we’ve updated the current image number, we call a showSlide()
function. That function iterates over the images
variable, and updates the transform
property on all the images one by one according to the formula that we discussed earlier.
Now, if you click on the “Next” button, you’ll see the images slide to the left, until you reach the last image. If you hit the left button again, the images will slide to the right until the first image is back in view. You’ll still see all the images. To see one image at a time, modify the style specification for the slider
element to the following:
#slider {
width: 600px;
height: 400px;
overflow: hidden;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.8);
display: flex;
}
Now, you should see one image at a time, and be able to slide to the next image. Implementing the previous image scrolling is just a matter of adding a few lines of code to JavaScript:
let imageNumber = 1
const numImages = 5
const sliderElement = document.getElementById('slider')
const nextButton = document.getElementById('next')
const prevButton = document.getElementById('previous')
let images
nextButton.addEventListener('click', nextImage)
prevButton.addEventListener('click', previousImage)
loadImages()
function loadImages() {
for (let i = 1 ; i <= numImages ; i++) {
const imgElement = document.createElement('img')
imgElement.src = `imgs/pic${i}.jpg`
sliderElement.appendChild(imgElement)
}
images = document.querySelectorAll('#slider img')
}
function showSlide() {
images.forEach( img => {
img.style.transform = `translateX(-${(imageNumber - 1) * 100}%)`
})
}
function nextImage() {
if (imageNumber < numImages) {
imageNumber++
}
else {
imageNumber = 1
}
showSlide()
}
function previousImage() {
if (imageNumber > 1) {
imageNumber--
}
else {
imageNumber = 5
}
showSlide()
}
We acquired an object referencing the “Previous” button. We hooked up a click event handler to it, which is very similar to that for the “Next” button, except we’re decrementing the current image number. If it reaches 1, we wrap it around to 5.
That’s all folks!
That concludes our project. You may download the entire source code along with 5 free images from this github repository. If you prefer copy-paste, here’s the HTML:
<!DOCTYPE html>
<html>
<head>
<title>Image slider</title>
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="fontawesome/css/all.min.css">
</head>
<body>
<h2>An image slider</h2>
<section class="centered">
<div id="slider"></div>
<div class="btn-bar">
<button id="previous">
<i class="fas fa-backward"></i>
</button>
<button id="next">
<i class="fas fa-forward"></i>
</button>
</div>
</section>
<script src="app.js"></script>
</body>
</html>
Here’s the CSS:
*, ::before, ::after {
padding: 0;
margin: 0;
box-sizing: border-box;
}
body {
background: rgba(0, 0, 0, 0.15);
}
h2 {
text-align: center;
margin: 10px 0;
}
.centered {
margin: 0 auto;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 600px;
}
#slider {
width: 600px;
height: 400px;
overflow: hidden;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.8);
display: flex;
}
#slider img {
width: 100%;
transition: transform 1s ease;
}
.centered .btn-bar {
display: flex;
width: 50%;
justify-content: space-evenly;
margin: 10px 0;
}
and here’s the JavaScript:
let imageNumber = 1
const numImages = 5
const sliderElement = document.getElementById('slider')
const nextButton = document.getElementById('next')
const prevButton = document.getElementById('previous')
let images
nextButton.addEventListener('click', nextImage)
prevButton.addEventListener('click', previousImage)
loadImages()
function loadImages() {
for (let i = 1 ; i <= numImages ; i++) {
const imgElement = document.createElement('img')
imgElement.src = `imgs/pic${i}.jpg`
sliderElement.appendChild(imgElement)
}
images = document.querySelectorAll('#slider img')
}
function showSlide() {
images.forEach( img => {
img.style.transform = `translateX(-${(imageNumber - 1) * 100}%)`
})
}
function nextImage() {
if (imageNumber < numImages) {
imageNumber++
}
else {
imageNumber = 1
}
showSlide()
}
function previousImage() {
if (imageNumber > 1) {
imageNumber--
}
else {
imageNumber = 5
}
showSlide()
}