How To

Level Up Your HTML, CSS and JS With This Simple Drop-In Image Slider Project

20 Oct , 2017  

HTML Image SliderHey everyone, I’ve got a fun project to work through today.

Implementing a slider is one of the projects I recommend to new programmers looking for a project.

It’s simple to make a basic slider, but I wanted to make one that we can drop into any website and is easy to use for anyone familiar with a little HTML.

While we do this we’re going to cover:

  • How to create a JavaScript module
  • Using CSS transitions
  • Customizing JavaScript with HTML data attributes

Who this article is for:

This is an intermediate-level article, so I’m assuming you have a little familiarity with HTML, CSS and JavaScript.

If you don’t you’re in luck, because I’ve got articles on all of them right here on the site 🙂

You can find your HTML primer here, a CSS introduction here, and plenty of JavaScript articles here.

If you want all of this in a handy, free e-book, you can get that right here 🙂

Let’s get started!

What we’re going to build – a minimal image slider

Unlike a tiny hamburger, an image slider is used to display a series of images in an HTML page.

This is useful for portfolio and gallery sites.

I was driven to roll my own recently because the slider plugins available for WordPress were all giving me a headache!

(I feel better now).

Here’s an example of the simple slider, in action:

Slider 1Slider 2Slider 3Slider 4

Note that while I’m calling it a slider, the images don’t slide, they fade in and out. I find this a more soothing transition!

The slider is implemented with a few lines of HTML, a few more lines of CSS and around 70 lines of JavaScript. It could be less, but I wanted it as clear as possible without introducing comments.

Let’s start with the HTML:

HTML for the image slider

Here’s our HTML. There’s not much to it:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>The Minimal Slider</title>
    <link rel="stylesheet" href="min-slider.css">
    <script src="min-slider.js"></script>
  </head>
  <body>
    <div id="min-slider" data-slider-interval="2000">
      <img class="min-slider-img" src="img/1.jpg">
      <img class="min-slider-img transparent" src="img/2.jpg">
      <img class="min-slider-img transparent" src="img/3.jpg">
      <img class="min-slider-img transparent" src="img/4.jpg"> 
    </div>
  </body>
</html>

There’s not much happening here, right? The only things we really need to notice are:

  • We’re including the min-slider.css and min-slider.js files. Those are coming up next.
  • We’ve got a div with id=”min-slider”, and a custom data attribute data-slider-interval=”2000″.
  • There are four images inside the min-slider div. The first has class=”min-slider-img”, and the others have class=”min-slider-img transparent”.

We’ll see how these work soon.

Next, the CSS:

CSS for the image slider

Here’s our CSS:

.min-slider-img {
  max-width: 100%;
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  transition: opacity 1s ease-in-out;
}

.transparent {
  opacity: 0;
}

So what have we here? Our image style, .min-slider-img has:

  • max-width set to 100% to make sure our images don’t out-grow their container.
  • position: absolute; top 0; to load our images on top of each other. This makes it easier to transition them, and stops the web page being filled up with invisible images.
  • left: 50%; transform: translateX(-50%); this is a hack for centering an image with position: absolute;
  • transition: opacity 1s ease-in-out; to make the image fade in and out when we change its opacity. opacity indicates what we’d like to change (we’ll go from opacity 0 to 1), 1s means we want the transition to occur in one second, and ease-in-out describes how the transition should occur. See here for a description of easing functions with some fun examples.

The second class, transparent just sets opacity to zero, making the image invisible but still present in the DOM.

Lastly, we need the JavaScript to put all this together:

JavaScript for the image slider

Because the JavaScript is a but more involved than the HTML and CSS, I’ll build it up a bit at a time.

The JavaScript is going to be implemented as a module. The word module isn’t part of the JavaScript language – it’s just a way of organizing code to:

  • Hide details of how the module gets its job done
  • Present a simple interface for controlling the actions of the module.

A JavaScript module usually organized like this:

const ModuleName = (function() {

  // PRIVATE AREA
  // Variables and functions go here. They will be invisible
  // to code outside the module
  let examplePrivateValue = 0;
  function examplePrivateFunction() { console.log ('¡privado!'); }

  return {
    // PUBLIC AREA
    // Return a JavaScript object. Variables and functions here have
    // access to the private data and functions, but are accessible to
    // code outside the module:
    publicFunction: () => {
      examplePrivateValue += 1;
      examplePrivateFunction();
      return examplePrivateValue;
    },
  };
})();

console.log(ModuleName.publicFunction());
// ¡privado!
// 1

Which is to say, it provides an abstraction.

Here’s a first implementation of the image slider module JavaScript:

var MinSlider = (function() {
  const SLIDER_ID = "min-slider";
  const IMG_CLASS = "min-slider-img";
  const TRANSPARENT_CLASS = "transparent";
  const DEFAULT_INTERVAL = 2000;

  function getSliderElement() {
    return document.getElementById(SLIDER_ID);
  }

  let images = []
  function loadImages() {
    images = document.getElementsByClassName(IMG_CLASS);
    if (!images || images.length === 0) {
      throw new Error ("No slider images");
    }
  }

  let timerRef;
  function startSlider() {
    let interval = DEFAULT_INTERVAL;
    let ms = getSliderElement();
    if (ms) {
      interval = ms.dataset.sliderInterval;
    }
    timerRef = setInterval(incrementImage, interval);
  }

  let imgIdx = 0;
  function incrementImage() {
    images[imgIdx].classList.add(TRANSPARENT_CLASS);
    if (++imgIdx >= images.length) { imgIdx = 0; }
    images[imgIdx].classList.remove(TRANSPARENT_CLASS);
  }

  return {
    start:  () => {
      loadImages();
      startSlider();
    },
  };

})();

window.addEventListener("load", MinSlider.start);

Breaking down the image slider JavaScript

First, we create a module  definition:

var MinSlider = (function() {

  // Private things

  return {
    // Public things
  };
})();

Next, I’ve made some named constants to hold class and id names, and default values:

var MinSlider = (function() {
  const SLIDER_ID = "min-slider";
  const IMG_CLASS = "min-slider-img";
  const TRANSPARENT_CLASS = "transparent";
  const DEFAULT_INTERVAL = 2000;

  return {
    // Public things
  };

})();

To start the slider, there’s two things we need to do:

  • Find all the images that’ll be in the slider
  • Start an interval timer that’ll repeatedly:
    • Hide the current image by adding the transparent class to it
    • Display the next image by removing the transparent class from it

Here’s where we load the images:

  let images = []
  function loadImages() {
    images = document.getElementsByClassName(IMG_CLASS);
    if (!images || images.length === 0) {
      throw new Error ("No slider images");
    }
  }

And starting the interval timer:

  function getSliderElement() {
    return document.getElementById(SLIDER_ID);
  }

  let timerRef;
  function startSlider() {
    let interval = DEFAULT_INTERVAL;
    let ms = getSliderElement();
    if (ms) {
      interval = ms.dataset.sliderInterval;
    }
    timerRef = setInterval(incrementImage, interval);
  }

Note the use of dataset. Remember how our HTML div has a custom data attribute?

<div id="min-slider" data-slider-interval="2000">

Once we’ve retrieved an element, we can access a data attribute on it using the dataset collection. The attribute will be renamed without hyphens and the leading data, and then transformed to camelCase.

So data-slider-interval becomes sliderInterval.

Once we’ve done that we’re free to set up an interval time using setInterval. This requires the function to move from one image to the next, which I’ve called incrementImage:

  let imgIdx = 0;
  function incrementImage() {
    images[imgIdx].classList.add(TRANSPARENT_CLASS);
    if (++imgIdx >= images.length) { imgIdx = 0; }
    images[imgIdx].classList.remove(TRANSPARENT_CLASS);
  }

This code uses the classList object to add and remove the transparency class from our images. Doing this will trigger the CSS transition for our min-slider-img rule:

transition: opacity 1s ease-in-out;

So one image is fading out while the next image is fading in, giving us a cross-fade.

Finally, we need to return the public interface for MinSlider:

  return {
    start:  () => {
      loadImages();
      startSlider();
    },
  };

And then start it once the page has loaded:

window.addEventListener("load", MinSlider.start);

That goes outside the MinSlider definition.

That’s a complete, but very minimal definition of an image slider.

Starting and stopping the image slider

But we do want to be able to start and stop the image slider. We’ll do this just by toggling the slider on and off when the user clicks it.

To do that, we’ll need to introduce a little more state to keep track of what the sliders doing, and a new function to stop the slider and a function to toggle sliding on and off:

  let sliding = true;
  function toggleSlider() {
    if (sliding) {
      stopSlider();
    } else {
      startSlider();
    }
    sliding = !sliding;
  }

  function stopSlider() {
    clearInterval(timerRef);
  }

We use clearInterval with the reference setInterval returned earlier to stop our slider.

There’s just one more thing to do! We need to add an event listener to our slide show so that toggleSlider is executed:

  function registerClickEvents() {
    let ms = getSliderElement();
    ms.addEventListener("click", toggleSlider);
  }

  return {
    start:  () => {
      loadImages();
      registerClickEvents();
      startSlider();
    },
  };

Now the image slider starts and stops when it’s clicked.

Here’s the final, complete JavaScript code:

var MinSlider = (function() {
  const SLIDER_ID = "min-slider";
  const IMG_CLASS = "min-slider-img";
  const TRANSPARENT_CLASS = "transparent";
  const DEFAULT_INTERVAL = 2000;

  function getSliderElement() {
    return document.getElementById(SLIDER_ID);
  }

  let images = []
  function loadImages() {
    images = document.getElementsByClassName(IMG_CLASS);
    if (!images || images.length === 0) {
      throw new Error ("No slider images");
    }
  }

  let timerRef;
  function startSlider() {
    let interval = DEFAULT_INTERVAL;
    let ms = getSliderElement();
    if (ms) {
      interval = ms.dataset.sliderInterval;
    }
    timerRef = setInterval(incrementImage, interval);
  }

  let imgIdx = 0;
  function incrementImage() {
    images[imgIdx].classList.add(TRANSPARENT_CLASS);
    if (++imgIdx >= images.length) { imgIdx = 0; }
    images[imgIdx].classList.remove(TRANSPARENT_CLASS);
  }

  
  let sliding = true;
  function toggleSlider() {
    if (sliding) {
      stopSlider();
    } else {
      startSlider();
    }
    sliding = !sliding;
  }

  function stopSlider() {
    clearInterval(timerRef);
  }

  function registerClickEvents() {
    let ms = getSliderElement();
    ms.addEventListener("click", toggleSlider);
  }

  return {
    start:  () => {
      loadImages();
      registerClickEvents();
      startSlider();
    },
  };

})();

window.addEventListener("load", MinSlider.start);

Going beyond the minimal image slider

While today’s example is a simple, minimal implementation, it’s still practical to use in real web pages.

But there’s a few things I’d change before doing that:

  • I’d define a better default behavior then throwing an error, or just doing nothing when the page has no slider elements or pictures on it.
  • Check that data-slider-interval really is a number.
  • The slider really needs to hint that the slider can be started and stopped by clicking on it.
  • It also needs navigation – a way of cycling through the images manually both forwards and backwards, and maybe some thumbnails as well for jumping to specific images.

Nonetheless, we’ve covered a lot of ground in today’s article. We’ve seen how to:

  • Implement a JavaScript module
  • How to use CSS transitions
  • An example of custom HTML data attributes in action

Is there anything you’d add to the slider? Let me know in the comments.

If you enjoyed the article, feel free to share on social media!

By


Leave a Reply

Your email address will not be published. Required fields are marked *