Creating A Before And After Image Slider With JavaScript
In the field of web development, user engagement and interactive design play pivotal roles in creating a memorable online experience. One powerful technique that captivates audiences and tells compelling visual stories is the Before-and-After Image Slider.
This component allows users to seamlessly compare two images by sliding between them, unveiling transformations, enhancements, or the passage of time. In this guide, you will learn how to build the image slider using just HTML, CSS and Vanilla JavaScript.
Setting Up The Development Environment
To get started, create a new folder. On UNIX systems and Windows, you can do so with the mkdir
command. In this folder, add the following files: index.js
, index.html
and app.css
. Open the index.html
file and add the following starter code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="app.css" />
<title>Before and After Image Slider</title>
</head>
<body>
<h1>Before and After Image Slider</h1>
<form>
Add "before" image:
<input type="file" accept="image/*" name="" id="before-selector" onchange="updateImage()"/>
<br />
Add "after" image:
<input type="file" accept="image/*" name="" id="after-selector" onchange="updateImage()"/>
</form>
<div class="image-container">
<input type="range" name="" id="" min="0" max="100" value="50" tabindex="-1"/>
<div class="image-after"></div>
<div class="image-before"></div>
</div>
<script src="index.js"></script>
</body>
</html>
In the code block above, there is form
element that holds two input
elements, with each one of them having an onchange
event handler that calls an updateImage()
function.
Next, you have an "image-container" div
, that holds three additional elements, a "range" input
element and two divs
that will hold the "before" and "after" image respectively.
The input
element with type "range", has a default value of 50, and the tabindex
attribute is there to ensure that the input
element does not receive tab focus.
Adding Some Styling
Right now everything looks bland, so open up the app.css
file, and add the following:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
form {
display: flex;
flex-direction: column;
gap: 10px;
margin-bottom: 10px;
}
body {
position: absolute;
left: 50%;
transform: translateX(-50%);
}
.image-container {
display: flex;
margin-top: 10px;
height: 700px;
width: 1015px;
background-color: transparent;
}
input[type='range'] {
-webkit-appearance: none;
appearance: none;
background: transparent;
position: absolute;
width: inherit;
padding: 0px;
margin-top: 360px;
z-index: 100;
}
input[type='range']::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
border-radius: 50%;
margin-top: -10px;
background-color: white;
cursor: grab;
height: 1.1rem;
width: 1.1rem;
}
input[type='range']::-webkit-slider-thumb:active {
background-color: rgb(231, 227, 227);
cursor: grabbing;
}
.image-before {
position: absolute;
padding: 0;
margin-left: 8.5px;
background-image: url('https://cdn.pixabay.com/photo/2023/08/16/23/47/annas-hummingbird-8195150_1280.jpg');
background-repeat: no-repeat;
background-size: cover;
box-shadow: 5px 0px 9px 1px #fffefe11;
height: inherit;
width: 1000px;
z-index: 10;
border-right: solid 0.5px rgba(255, 255, 255, 0.425);
}
.image-after {
position: absolute;
margin-left: 8.5px;
background-image: url('https://cdn.pixabay.com/photo/2016/11/23/18/27/hummingbird-1854225_640.jpg');
background-repeat: no-repeat;
background-size: cover;
height: inherit;
width: 1000px;
/* z-index: -10; */
}
From the CSS above, there are four important things you should pay attention to: The styling for the input
element of type "range",image-container
and image-before
and image-after
.
The image-before
and image-after
divs have their positions set to absolute
, with image-before
overlaying image-after
. This is important, because it helps sell the illusion of a smooth transition between images as the user moves the slider.
The "range" input
element is important because you can hook onto it with JavaScript and using the "value" attribute, to determine how wide an image should be. Notice how the default styling for input[type='range']
is overridden with appearance: none
.
Making Things Functional With JavaScript
Moving on to JavaScript, open the index.js
file, select all the necessary elements from the Document Object Model (DOM) and bind them to variables, like this:
let slider = document.querySelector("input[type='range']");
let beforeImage = document.querySelector('.image-before');
let afterImage = document.querySelector('.image-after');
let beforeSelector = document.querySelector('#before-selector');
let afterSelector = document.querySelector('#after-selector');
Next, using the getComputedStyle
function, retrieve the width of beforeImage
and bind it to the beforeImageWidth
variable. Remember that getComputedStyle
returns the width alongside it's unit (which you do not need in this case). In this case, since the unit is "px", you can use the slice
method to remove the unwanted unit like this:
let beforeImageWidth = getComputedStyle(beforeImage).width.slice(0, getComputedStyle(beforeImage).width.indexOf('p'));
Next, add an event listener to slider
that will listen for "input" events on the element. In the event listener's callback function, call the updateImage
function and pass in the target element as a parameter:
slider.addEventListener('input', (e) => {
adjustImage(e.target);
});
Finally, add define the adjustImage
function like this:
function adjustImage(target) {
beforeImage.style.width = `${(target.value / 100) * beforeImageWidth}px`;
}
In the code block above, the adjustImage
function takes in the target
parameter uses the value
property to modify the width of beforeImage
.
Finally, call the adjustImage
function, and pass in slider
as a parameter. This will ensure consistency by automatically setting the width of beforeImage
on page load:
adjustImage(slider);
If you followed the guide correctly, the results should look something like this:
Taking Things Further By Adding Upload Functionality
The image slider works fine, but right now, there is currently no way to modify the before and after images without digging through the app.css
file and modifying the hard-coded values in the background
properties of image-before
and image-after
. To make things dynamic, create an updateimage
function like this:
function updateImage() {}
Check if #beforeSelector
contains any files, then instantiate the FileReader
object and call the readAsDataURL
method.
if (beforeSelector.files && beforeSelector.files[0]) {
const reader = new FileReader();
reader.onload = function (e) {
// Display the selected image
beforeImage.style.background = `url("${e.target.result}")`;
};
reader.readAsDataURL(beforeSelector.files[0]);
}
Do the same for #afterSelector
, like this:
if (afterSelector.files && afterSelector.files[0]) {
const reader = new FileReader();
reader.onload = function (e) {
// Display the selected image
afterImage.style.background = `url("${e.target.result}")`;
};
reader.readAsDataURL(afterSelector.files[0]);
}
Returning to the browser, you should be able to select the before and after images from the front-end, without needing to modify the hard-coded CSS values:
Things To Consider When Building An Image Slider
While this guide covered the basics, in a real world application, there many things to consider when building an image slider, like responsive design, support for touch gestures, accessibility.
Note that not all users are the same, and you should always strive to provide alternatives for users with disabilities, even if it comes at the cost of aesthetics. Lastly, keep it simple and focused – don't overwhelm your audience with too many images or flashy transitions. Striking the right balance between aesthetics and functionality is the secret sauce for an effective image slider.