Making a Dynamic Slider
Tags: beginners
Published: 2019-09-03 10:37:41

# 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:
```css
	margin-left:
```

The CSS property Transition:
```css
	transition:
```

And the Javascript Function setInterval
```javascript
	setInterval(()=>{
	},TimeInMilisecondsHere)
```


# 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:
```html
<body>

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

		<div class="slider-container">

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

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

</body>
```
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.
```css
.slider-container{
	margin: 0 10px 15px 10px;
	height:265px;
	overflow:hidden;
}
```

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.

```css
.slider-size-regulator{
	height:100%;
	overflow:hidden;
	display:flex;
}
```

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.

```html
<div class="slider-item" style="background-image:url(assets/images/featuredimg1.jpg)"></div>
```
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.

```css
.slider-item{
	height:100%;
	background-position: center;
	background-repeat: no-repeat;
	background-size:cover;
}
```

# 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

```javascript
document.addEventListener('DOMContentLoaded',()=>{
 // Once the DOM has finished loading, all code in here will be run
});
```
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

```javascript
var sliderInterval = "";
let numberOfSlidesBeingDisplayed = 2;
let variableSliderWidth = contentWrapperElement.offsetWidth;
```

### Element References
These variables are simply references to our Html elements so that we can interact and listen to events happening.
```javascript
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');
```

# 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.
```javascript
sliderContainerElement.style.width = (contentWrapperElement.offsetWidth) + `px`;
```

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.
```html
<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>
```

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.
```javascript
sliderItemElements.forEach((sliderItemElement) => {
		sliderItemElement.style.width = variableSliderWidth / numberOfSlidesBeingDisplayed + `px`; 
	})
```

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 `sliderSizeRegulatorElement`s width.

```javascript
sliderSizeRegulatorElement.style.width = ((variableSliderWidth / numberOfSlidesBeingDisplayed) * sliderItemElements.length) + `px`;
```

# 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

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

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

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

		sliderInterval = setInterval(() => {
		}, 2500)
	}
```

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

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

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

```javascript
sliderSizeRegulatorElement.addEventListener('transitionend', () => {
	sliderSizeRegulatorElement.style.transition = "unset";
});
```

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.

```javascript
sliderSizeRegulatorElement.addEventListener('transitionend', () => {
	sliderSizeRegulatorElement.style.transition = "unset";
	if(sliderSizeRegulatorElement.childNodes[0].nodeName == "#text"){
		sliderSizeRegulatorElement.removeChild(sliderSizeRegulatorElement.childNodes[0]);
	};
});
```
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.

```javascript
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]);
	};
```

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

```javascript
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;
	}
};
```
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.

```javascript
let firstChild = sliderSizeRegulatorElement.removeChild(sliderSizeRegulatorElement.childNodes[0]);
sliderSizeRegulatorElement.insertAdjacentElement('beforeend', firstChild);
sliderSizeRegulatorElement.style.marginLeft = 0;
```

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.

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

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

```javascript
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();
```

## 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.