Creating A Before And After Image Slider With JavaScript

Slide It Like You Mean It

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:

via GIPHY

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:

via GIPHY

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.