Introduction

In the previous chapter we constructed the HTML, as well as the CSS for our slider from scratch, and it is now that we will dig deeper into its last requirement i.e JavaScript.

So before you start on with this chapter make sure you are familiar with JavaScript. If you are not please consider taking our JavaScript Course.

Buttons and events

Recall that the HTML code in the previous chapter had two navigation buttons for moving across the slider - one for going right/forward and the other one for going left/backward.

Uptil the previous chapter this was just their purpose, but now it will become their action when they are clicked. Interactivity in action!

So let's go on and give our buttons each a click event handler:

// select the navigation buttons
var sliderNav = document.getElementsByClassName("slider-nav");

sliderNav[0].onclick = function() {}
sliderNav[1].onclick = function() {}
If you don't know what are event handlers, or the whole concept of events in JavaScript, then you can consider going over our JavaScript Events unit.

First we select the buttons (in line 2) and then assign each one a click handler separately.

The forward button shall hide the current slide and show the next one. Similarly, the backward button shall hide the current slide and show the previous one.

Consider the code below which accomplishes this in a very elementary manner:

var index = 0;

var slides = document.getElementsByClassName("slides");
var sliderNav = document.getElementsByClassName("slider-nav");

sliderNav[0].onclick = function() {
    slides[index].style.display = "none";
    slides[--index].style.display = "block";
};

sliderNav[1].onclick = function() {
    slides[index].style.display = "none";
    slides[++index].style.display = "block";
}

Elaborating on the variables:

  1. index is used to track the slide we are currently on.
  2. slides selects all the individual slides.
  3. sliderNav selects both the navigation buttons.

In the handler function for sliderNav[0] (Backward button), from line 6 - 8, we take the current slide we are currently on and hide it. Then we decrement index to point to the previous slide and finally show it.

The same goes for the sliderNav[1] (Forward button) handler, apart from that it increments index.

And we have a working slider!

Simple Interactive Slider

However it is not without errors and is therefore not a perfect slider. Next we will see how we can improve this slider code by removing these errors and coming under the hood of best practices.

DRY (Dont Repeat Yourself)

If you notice, in the code above we are merely repeating statements that show and hide the slides (in lines 6, 8 and 12, 14).

Whenever developing programs the DRY principle shall be in mind:

We should keep from repeating code in programs, as much as we can. Instead of repeating stuff we could use functions to group repeating code and likewise use the functions.

This makes our programs extensible and much easier to maintain.

So going back to our slider's script, we will now create a navigateSlider() function and put the repeating statements to, navigate the slider, within it.

And to get this function to work correctly we'll need another global variable, prevIndex, to keep track of the previous slide, as can be seen below:

var index = 0;
var prevIndex = 0; // keep track of prev slide

var slides = document.getElementsByClassName("slides");
var sliderNav = document.getElementsByClassName("slider-nav");

function navigateSlider() {
    slides[prevIndex].style.display = "none";
    slides[index].style.display = "block";
    prevIndex = index;
}

sliderNav[0].onclick = function() {
    index--;
    navigateSlider();
};
sliderNav[1].onclick = function() {
    index++;
    navigateSlider();
}

In the navigateSlider() function above, we first hide the slide at position prevIndex and then show the one at position index.

This follows from the fact that prevIndex holds the index of the previous slide, which needs to be hidden; whereas index holds the index for the new slide, which needs to be shown.

After getting this done we set prevIndex equal to index. This is the most important statement of our function, since it saves the index of the new slide brought in place, and makes the next next navigation know the previous slide.

Also notice that this statement will work properly only at the bottom of the function - if we set prevIndex = index at the beginning we will be unintentionally altering the CSS for the same element.

Dealing with extreme slides

In the code above we haven't dealt with cases where the extreme slides, or in other words the first and last slides, are reached. In these cases we at least don't want to increment or decrement index because doing this can throw errors.

How?

At the start we have index = 0, and therefore pressing the backward button will decrement it to -1. Consequently the statement slides[index].style.display = "block"; will throw an exception since there's no index such as -1!

Similarly we have index = 2 at the last slide, and therefore pressing the forward button will increment it to 3. Consequently the statement slides[index].style.display = "block"; will once again throw an exception since there's no index such as 3 (only 0, 1 and 2)!

So what we can do instead is use conditional statements to check for these extreme positions and then either repeat the cycle or break the flow.

Let's see what both these mean.

Repeat the cycle

Suppose that someone is on the last slide and at this point presses the forward button. In this repeat-the-cycle behaviour what we will do is take him to the first slide again, and thus repeat the navigation cycle.

In this way the slider will keep on navigating with the forward button, without any bound.

The same would also apply for the backward button on the first slide - in this case we would repeat the cycle by showing the last slide, and obviously hiding the first one.

But how do we check for these extreme cases in code? For a hint, these cases will be accomplished by index - it will be equal to some value specifically signifying the first and last slides. So did you get it?

For the first slide index would be 0 and for the last one it would be the number of slides - 1.

We can layout this check for index within our navigateSlider() function, and apply a behaviour according to the value.

With all this theory in place we arrive at the following code:

var slidesLength = slides.length; // number of slides

function navigateSlider() {
    // check for extreme slides
    if (index === -1) index = slidesLength - 1;
    else if (index === slidesLength) index = 0

    slides[prevIndex].style.display = "none";
    slides[index].style.display = "block";
    prevIndex = index;
}

We have different checks for different handlers - for Previous we check when the first slide has reached and for Next we check when the last slide is reached. The variable slidesLength, holding the total number of slides, is used in determing the last slide's index.

repeat-the-cycle slider

Break the flow

The second option we have for the extreme slides case is to break the flow.

As the name implies it will simply stop the navigation when the slider reaches the first or last slides, unlike the first choice where we can navigate infinitely.

Visually this would mean giving the buttons a disabled look, which can be done by assigning the disabled attribute and styling its :disabled psuedo state with a low value of opacity.

The following code accomplishes this task:

.slider-nav:disabled {
    /* make the button almost invisible */
    opacity: 0.4
}

Although many browsers automatically reduce the opacity on disabled input fields and buttons, the CSS code above goes even beyond that for an even lower level of opacity.

var slidesLength = slides.length; // number of slides

function navigateSlider() {
    if (index === -1) index = 0;
    else if (index === slidesLength) index = slidesLength - 1;

    sliderNav[0].disabled = (index === 0) ? true : false;
    sliderNav[1].disabled = (index === slidesLength - 1) ? true : false;

    slides[prevIndex].style.display = "none";
    slides[index].style.display = "block";
    prevIndex = index;
}

Here we, once again, first rectify the extreme cases and normalize index to 0 when it becomes -1; and to slidesLength - 1 when it becomes equal to slidesLength.

Additionally we also check when the index is 0 (first slide shown) or slidesLength - 1 (last slide shown), and disable the backward or forward buttons correspondingly. When index changes from either of these values, we enable the respective button.

break-the-flow slider

Moving on

With all this we now have, as we said earlier, a perfect slider working flawlessly. Add as many number of .slides you like and the slider will scale according to them.

Well despite the fact that this slider was a simple one, it arguabaly had a lot of details to deal with, near overwhelming, didn't it? So you can surely relate to the fact yourself, that what is coming up next will be much difficult and detailed than this simple slider's simple working!!

In the coming chapters we will move on to add numerous features to our slider including the legacy swiping interaction which would be literally 10 times as much information on this page, and even 10 times more fun!

To keep things in order, let's go over adding a simple feature to our slider that is pagination in the next chapter.