How To Create a Before and After Image Slider With Vanilla JavaScript

David Uzondu - Dec 1 '23 - - Dev Community

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>
Enter fullscreen mode Exit fullscreen mode

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; */
}
Enter fullscreen mode Exit fullscreen mode

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 use 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");
Enter fullscreen mode Exit fullscreen mode

Next, using the getComputedStyle function, retrieve the width of beforeImage and bind it to the beforeImageWidth variable. Remember that getComputedStyle returns the width alongside its 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")
);
Enter fullscreen mode Exit fullscreen mode

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);
});
Enter fullscreen mode Exit fullscreen mode

Finally, add define the adjustImage function like this:

function adjustImage(target) {
  beforeImage.style.width = `${(target.value / 100) * beforeImageWidth}px`;
}
Enter fullscreen mode Exit fullscreen mode

In the code block above, the adjustImage function takes in the target parameter and 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);
Enter fullscreen mode Exit fullscreen mode

Taking Things Further By Adding Upload Functionality.

The image slider works fine, but right now, there is 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(){

}
Enter fullscreen mode Exit fullscreen mode

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]);
}
Enter fullscreen mode Exit fullscreen mode

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]);
}
Enter fullscreen mode Exit fullscreen mode

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 are many things to consider when building an image slider, like responsive design, support for touch gestures, and 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.

. . . . .