DEV Community

Mikkel
Mikkel

Posted on

Making a Dynamic Slider

Introduction

In this tutorial we will be making a dynamic slider displaying between 1 and 3 slider-items at a given time depending on the size of the viewport.

The slider will slide to the left and loop infinitely.

To make this "Slide to the left" there are three crucial components in play:

The CSS property Margin, more specifically Margin-Left:

    margin-left:
Enter fullscreen mode Exit fullscreen mode

The CSS property Transition:

    transition:
Enter fullscreen mode Exit fullscreen mode

And the Javascript Function setInterval

    setInterval(()=>{
    },TimeInMilisecondsHere)
Enter fullscreen mode Exit fullscreen mode

Getting Set up and Ready

Alright so to begin with you need to boot up your favorite code editor and make a directory for this project, once done we will need to create three different files as follows:

  • index.html
  • style.css
  • script.js

Fantastic! now open your index.html file in your code editor and type an Exclamation mark in the file, a dropdown looking like this https://imgur.com/a/4JBIsrN should pop down, choose the very first one. You should now have a HTML file with some basic HTML in it.

Now that your Html file is ready you can go ahead and link your stylesheet and your javascript

Linking your style.css to your index.html looks like this: https://imgur.com/a/QBhVPHF

Linking your script.js to your index.html looks like this:
https://imgur.com/a/6LnxA2F

Building the basic HTML structure

Alright so by now you should have a HTML file with a stylesheet and a script bound to it, so now we can begin building the basic HTML structure that is required for the slider to be dynamic.

Inside your body element, make three div elements with classes looking like this:

<body>

    <div class="slider-content-wrapper">

        <div class="slider-container">

            <div class="slider-size-regulator">

            </div>
        </div>
    </div>

</body>
Enter fullscreen mode Exit fullscreen mode

The reason for having a wrapper for the slider-container will be made clear later on but for now, we simply need a structure looking like this.

Our slider-container will act as the Visual slider on the website, it will display the number of slider-items we are going to choose later on in our javascript.

Our slider-size-regulator will act as the infinitely wide slider-item container, It will adjust in width according to how many slider-items (images) we have got and how wide each image is, timed by the slider-item count we choose to display on the visual slider.

Applying the required styling to visually create the slider

Styling the slideshow is fairly straight forward, we only need a few rules in our CSS file to create the static visual appearance that we are after.

First off we want to define a height for our visual slider-container element, we need to provide a static height as a reference point so that we later can adjust it to the viewport size aswell as the number of slider-items being displayed:

Overflow:hidden is going to let us go beyond the provided elements width without making a horizontal scroll bar appear on our site. As this is our visual slider element it will be given a width later on calculated by some javascript code.

.slider-container{
    margin: 0 10px 15px 10px;
    height:265px;
    overflow:hidden;
}
Enter fullscreen mode Exit fullscreen mode

Now for our slider-size-regulator element, this is the element that is going to have a possibly-infinite width depending on the amount of slider-items.

We want this element to have the same height as it's parent element, the slider-container, so we will be giving it a height property with a value of 100%, It will also have overflow:hidden for the same reasons as stated above.

Our slider-size-regulator should also have display:flex, now this might not seem obvious and that is because it really isn't!

Display:flex will remove all the small gaps between each and every slider-item in the slider-size-regulator element, this happens because display flex collapses when there is no content to fill the empty space, unlike display:block which simply exists with or without content provided. The importance of this will become clear later on.

.slider-size-regulator{
    height:100%;
    overflow:hidden;
    display:flex;
}
Enter fullscreen mode Exit fullscreen mode

The slider-items itself will be individual div's within the slider-size-regulator. What we aim to do with this is to use inline styling on each hardcoded div to provide it with the image we want to be shown, this is far from perfect and is still a work in progress but it will do for now.

<div class="slider-item" style="background-image:url(assets/images/featuredimg1.jpg)"></div>
Enter fullscreen mode Exit fullscreen mode

Having our images as hardcoded background-images allows us to use the numerous background css properties that exists.

We are going to give our slider-items four different css properties: Height, Background-position, Background-repeat, and Background-size.

The height property simply forces the slider-item to fill as much as it's parent in height.

The Background-position:center finds the center of the image and aligns it with the center of the HTML element.

The Background-repeat:no-repeat prevents the image from repeating, should the image not fill the entire element's height or width.

The Background-size:cover forces the image to cover the entire HTML elements provided space.

.slider-item{
    height:100%;
    background-position: center;
    background-repeat: no-repeat;
    background-size:cover;
}
Enter fullscreen mode Exit fullscreen mode

Making the slider slide with Javascript

To begin with, we want to listen on when the DOM has finished loading, to do that we make an event listener, like this

document.addEventListener('DOMContentLoaded',()=>{
 // Once the DOM has finished loading, all code in here will be run
});
Enter fullscreen mode Exit fullscreen mode

Now that we have our eventListener, it is important that ALL code we do from here on out only exists within the eventListener

We can now create our references to our HTML Elements, and our miscellaneous variables.

Miscellaneous Variables

These 3 variables are fulfilling 3 different purposes, the sliderInterval one is set to contain an empty string, however, what it contains at this moment is not of importance! The important thing here is that it is scoped in such a way that you can get a hold of it almost anywhere, this will make sense later on.

The numberOfSlidesBeingDisplayed variable is simply so that you don't have to change the number many different places but instead can change it in one place only.

The variableSliderWidth is given a number value from contentWrapperElement, that number value is equal to the pixel width of the contentWrapperElement, that is calculated through the offsetWidth Property

var sliderInterval = "";
let numberOfSlidesBeingDisplayed = 2;
let variableSliderWidth = contentWrapperElement.offsetWidth;
Enter fullscreen mode Exit fullscreen mode

Element References

These variables are simply references to our Html elements so that we can interact and listen to events happening.

let contentWrapperElement = document.querySelector('.slider-content-wrapper');
    let sliderContainerElement = document.querySelector('.slider-container');
    let sliderItemElements = document.querySelectorAll('.slider-item');
    let sliderSizeRegulatorElement = document.querySelector('.slider-size-regulator');
Enter fullscreen mode Exit fullscreen mode

Calculating element properties

Now that we've got references to the HTML elements that we need, we can start calculating the required properties.

First, off we are going to provide our Visual slider sliderContainerElement with a dynamic width, we do that by using the default width of our contentWrapperElement which is the viewport width.

Using the property "offsetWidth" we can provide our sliderContainerElement with the exact same width contentWrapperElement has, and then we concatenate a string containing 'px' to make it a pixel value.

sliderContainerElement.style.width = (contentWrapperElement.offsetWidth) + `px`;
Enter fullscreen mode Exit fullscreen mode

Next, we are going to run through a nodelist of HTML elements, specifically our slider-item div's, your Html should look like this by now.

<body>
    <div class="slider-content-wrapper">
        <div class="slider-container">
            <div class="slider-size-regulator">
                <div class="slider-item" style="background-image:url(assets/images/featuredimg1.jpg)"></div>

                <div class="slider-item" style="background-image:url(assets/images/featuredimg2.jpg)"></div>

                <div class="slider-item" style="background-image:url(assets/images/featuredimg3.jpg)"></div>

                <div class="slider-item" style="background-image:url(assets/images/featuredimg4.jpg)"></div>
            </div>
        </div>
    </div>
</body>
Enter fullscreen mode Exit fullscreen mode

Now we can make a forEach loop on our sliderItemElements variable and run through our nodelist of HTML elements.

This is where our miscellaneous variables come into play, we are going to use the numerical value stored in our variableSliderWidth and divide it by the number stored in numberOfSlidesBeingDisplayed, then we want to concatenate that with a string containing 'px' to make it a pixel value, once that is calculated it will be the width of a single slider-item.

Because our variableSliderWidth variable got it's width from the same calculation as our sliderContainerElement it is safe to assume that they are equally wide, and so we can control the number of slider-items we want displayed simply by changing the numberOfSlidesBeingDisplayed variable.

sliderItemElements.forEach((sliderItemElement) => {
        sliderItemElement.style.width = variableSliderWidth / numberOfSlidesBeingDisplayed + `px`; 
    })
Enter fullscreen mode Exit fullscreen mode

Now we are going to calculate the width of our sliderSizeRegulatorElement, this can, in theory, be infinitely long, and because of overflow:hidden which we talked about earlier, it won't create a horizontal scroll bar.

To make the sliderSizeRegulatorElement wide enough to contain all the slider-items without wrapping, we need to provide it with both the number of slider-items present but also their actual width

to find the actual width of a slider-item we are going to use the same calculation as we used in the forEach Loop, but we are going to wrap it in parentheses simply to force that calculation to happen first, then we times it by the length of the sliderItemElements nodelist, which will give us the total width of all the slider-items, which in turn becomes our sliderSizeRegulatorElements width.

sliderSizeRegulatorElement.style.width = ((variableSliderWidth / numberOfSlidesBeingDisplayed) * sliderItemElements.length) + `px`;
Enter fullscreen mode Exit fullscreen mode

Functions, EventListeners and Conditions

Alright, so now that we've got the HTML structure we need, the styling from CSS, and the calculations to fit any device regardless of size through javascript, we can start making the slider slide!

We are going to define a function at the very bottom of our DOMContentLoaded EventListener called slideImagesByInterval, within that function we will be making a new miscellaneous variable called sliderMovedDistance, this new variable will calculate the width of a single slider-item the exact same way as our forEach did earlier, with this information we now know how much we want to move the slider to the left for it to have moved exactly the same pixel length as the width of a single slider-item, thus making it fully dynamic on any type of device

function slideImagesByInterval() {
    let sliderMovedDistance = variableSliderWidth / numberOfSlidesBeingDisplayed;
    };
Enter fullscreen mode Exit fullscreen mode

Now within the same function, but underneath the variable we just created, we want to start an interval. We made a variable earlier in a scope that can be reached by all our code, named sliderInterval, we are now going to overwrite the empty string within it with an actual interval that will make the slider slide

function slideImagesByInterval() {
        let sliderMovedDistance = variableSliderWidth / numberOfSlidesBeingDisplayed;

        sliderInterval = setInterval(() => {
        }, 2500)
    }
Enter fullscreen mode Exit fullscreen mode

Inside that interval function we are going to add some CSS properties to our sliderSizeRegulatorElement, We want the slider to transition in a smooth motion, as opposed to it being yanked to the side, so we are going to use the transition property, but we also need to tell it how far it has to go and which direction, to do this we are going to be using margin-left, specifically minus margin-left.

By using the variable we created at the top of this function we know how wide a slider-item is, and so we know how far we want the minus margin-left to drag the container, all we need to do at that point is to concatenate the 'px' string to let it know that it is a pixel value

sliderInterval = setInterval(() => {
    sliderSizeRegulatorElement.style.transition = "all 0.3s";
    sliderSizeRegulatorElement.style.marginLeft = -sliderMovedDistance + 'px';
}, 2500);
Enter fullscreen mode Exit fullscreen mode

At this point, all we need to do for the slider to slide is to call the function in our DOMContentLoaded eventListener, however for this to work in its current state the function call needs to be positioned in such a way that the variables it depends upon has already been declared. We need to call the function just beneath the line where we calculated the width of the sliderSizeRegulatorElement.

The slider now slides but it doesn't loop! it will eventually run out of images and will simply shuffle through empty slider-items. Let's make the slider loop properly.

To make the slider loop we are going to take out the first slider-item in the nodelist and attach it to the end of the nodelist every time the interval occurs. This way we will have an endless slider that just keeps going in a loop.

To make all this happen we first need to listen for when the slider transition has ended, meaning every time the slider has moved the width of a slider-item

sliderSizeRegulatorElement.addEventListener('transitionend', () => {
    sliderSizeRegulatorElement.style.transition = "unset";
});
Enter fullscreen mode Exit fullscreen mode

For the following steps to work, we are going to need some conditions to deal with the possible whitespace in the HTML structure.

We are checking if the first element's nodeName in the nodelist is "#text", if that is the case then we want to remove it from the nodelist, this is because it is preventing the slideshow from rearranging the slides to make an infinite loop.

sliderSizeRegulatorElement.addEventListener('transitionend', () => {
    sliderSizeRegulatorElement.style.transition = "unset";
    if(sliderSizeRegulatorElement.childNodes[0].nodeName == "#text"){
        sliderSizeRegulatorElement.removeChild(sliderSizeRegulatorElement.childNodes[0]);
    };
});
Enter fullscreen mode Exit fullscreen mode

Once we have removed the whitespace from the nodelist, the very first index of the nodelist will now be a slider-item, we are going to declare a new variable that we call firstChild which will remove the slider-item from the nodelist, and store it.

sliderSizeRegulatorElement.addEventListener('transitionend', () => {
    sliderSizeRegulatorElement.style.transition = "unset";
    if(sliderSizeRegulatorElement.childNodes[0].nodeName == "#text"){
        sliderSizeRegulatorElement.removeChild(sliderSizeRegulatorElement.childNodes[0]);
        let firstChild = sliderSizeRegulatorElement.removeChild(sliderSizeRegulatorElement.childNodes[0]);
    };
Enter fullscreen mode Exit fullscreen mode

Now that we have removed the very first slider-item from the nodelist and stored it in our firstChild variable, we are going to use the insertAdjacentElement method to insert our stored slider-item just before the nodelist ends, which means at the very end of the nodelist, this will enable the slider to loop infinitely.

At the same time, we are going to set the margin-left property to 0.
and as the very last thing in our If statement we will be typing return; at the bottom

sliderSizeRegulatorElement.addEventListener('transitionend', () => {
    sliderSizeRegulatorElement.style.transition = "unset";
    if(sliderSizeRegulatorElement.childNodes[0].nodeName == "#text"){
        sliderSizeRegulatorElement.removeChild(sliderSizeRegulatorElement.childNodes[0]);
        let firstChild = sliderSizeRegulatorElement.removeChild(sliderSizeRegulatorElement.childNodes[0]);
        sliderSizeRegulatorElement.insertAdjacentElement('beforeend', firstChild);
        sliderSizeRegulatorElement.style.marginLeft = 0;
        return;
    }
};
Enter fullscreen mode Exit fullscreen mode

This also needs to work if the first nodeName in the nodelist is NOT "#text", so inside the addEventListener right after the If statement we are going to duplicate the code in the If statement that did the storing and attatching aswell as the resetting of the margin.

let firstChild = sliderSizeRegulatorElement.removeChild(sliderSizeRegulatorElement.childNodes[0]);
sliderSizeRegulatorElement.insertAdjacentElement('beforeend', firstChild);
sliderSizeRegulatorElement.style.marginLeft = 0;
Enter fullscreen mode Exit fullscreen mode

Now that we have sorted the issues with the whitespaces, we've made the slider loop infinitely, and we have given the slider an interval, we can proceed onto the final task! We need to declare a function that will change the variable numberOfSlidesBeingDisplayed according to the size of the viewport.

We are going to declare a function that we call viewportWidth, inside that function declare a variable called viewportWidth750, This variable will contain our Javascript media query which will help us figure out how many slides to display based on the viewport width

Inside the function, based on based on the variable we just created we are going to make a condition stating that if the viewport is smaller than 750px it the numberOfSlidesBeingDisplayed should be set to 1 making the entire slideshow adapt and calculate the appropriate size for the slider-item as well as the appropriate distance the slideshow needs to slide for it to go to the next slider-item in the nodelist.

If the viewport is above 750px, do the same but with numberOfSlidesBeingDisplayed set to 3.

function viewportWidth(){
    let viewportWidth750 = window.matchMedia("(max-width: 750px)");
    if(viewportWidth750.matches){
        numberOfSlidesBeingDisplayed = 1;
        sliderContainerElement.style.height = 210+'px';
    }   
    else{
        numberOfSlidesBeingDisplayed = 3; 
    }
}
Enter fullscreen mode Exit fullscreen mode

What is left now is to call this viewportWidth function the correct place, so we are going to call it directly under the Html Reference Variables we made, looking like this

var sliderInterval = "";
let numberOfSlidesBeingDisplayed = 2;

let contentWrapperElement = document.querySelector('.slider-content-wrapper');
let sliderContainerElement = document.querySelector('.slider-container');
let sliderItemElements = document.querySelectorAll('.slider-item');
let sliderSizeRegulatorElement = document.querySelector('.slider-size-regulator');
let variableSliderWidth = contentWrapperElement.offsetWidth - 20;
viewportWidthDetection();
Enter fullscreen mode Exit fullscreen mode

Problems and inconveniences with this approach

There are a few issues making a slider like this with hardcoded HTML

  • It isn't very pretty
  • It isn't very user-friendly
  • It is time-consuming to add and remove images
  • Because it is a nodelist, any whitespace in the HTML will generate and index a [text] within the nodelist itself, and so we are still going to have to make conditions that recognize this and takes care of it for the slider to work.

Top comments (2)

Collapse
 
nicklasspendler profile image
NicklasSpendler

Very good and well formulated tutorial! I now know how to make a slider :)

Collapse
 
giacomoiso profile image
James Gary

Note to anyone trying this tutorial: I couldn't get it to work, aaand...there doesn't seem to be a link to a CodePen or GitHub repo I could check my code against. So I guess I'm out of luck.