Our TODO list is as follows:
- combine handler variables
- pass carousel to disableInteraction
- pass carousel to moveCarouselTo
- examine remaining let variables
combine handler variables
This part is easy. Instead of passing two function parameters, we pass only one that contains them both.
function moveNextHandler(evt) {
// carousel.slide = moveNext(carousel.slide, carousel.isMoving);
carousel.slide = moveNext(carousel);
}
function movePrevHandler(evt) {
// carousel.slide = movePrev(carousel.slide, carousel.isMoving);
carousel.slide = movePrev(carousel);
}
//...
// function moveNext(slide, isMoving) {
function moveNext({slide, isMoving}) {
//...
// function movePrev(slide, isMoving) {
function movePrev({slide, isMoving}) {
Pass carousel to disableInteraction
The carousel object can now be passed to the disableInteraction function.
// function disableInteraction() {
function disableInteraction(carousel) {
//...
}
//...
// disableInteraction();
disableInteraction(carousel);
Pass carousel to moveCarouselTo
The moveCarouselTo function uses both slide and isMoving, so we can just use the one carousel parameter with that function instead.
Let’s start by passing both slide and isMoving to the moveCarouselTo function.
// function moveCarouselTo(slide) {
function moveCarouselTo(slide, isMoving) {
// Check if carousel is moving, if not, allow interaction
// if(!carousel.isMoving) {
if(!isMoving) {
//...
// moveCarouselTo(slide);
moveCarouselTo(slide, isMoving);
In order to pass carousel to the moveCarouselTo function, we want to update the carousel slide property before doing that. We have a couple of approaches that could be taken here. We could update the carousel object before calling moveCarouselTo, or we could move that moveCarouselTo call out of the moveTo function and into the event listener. That latter option is what I’ll do here today.
Currently the moveNext function has two responsibilities, to figure out the next slide, and to move to that next slide. That breaches the single-responsibility principle. We should split up the function so that it has only one main thing to take care of instead.
I’ll copy some of what moveNext does to a separate nextSlide function, and move the moveCarouselTo function into the event handler instead.
function nextSlide({slide, isMoving}) {
// Check if moving
if (!isMoving) {
// If it's the last slide, reset to 0, else +1
if (slide === (totalItems - 1)) {
slide = 0;
} else {
slide++;
}
}
return slide;
}
We can now replace moveNext with two different function calls in the event handler.
function moveNextHandler(evt) {
carousel.slide = nextSlide(carousel);
moveCarouselTo(carousel.slide, carousel.isMoving);
}
and the old moveNext function no longer being used, can be deleted.
We can do similar with the movePrev function too.
function movePrevHandler(evt) {
// carousel.slide = movePrev(carousel);
carousel.slide = prevSlide(carousel);
moveCarouselTo(carousel.slide, carousel.isMoving);
}
//...
function prevSlide({slide, isMoving}) {
// Check if moving
if (!isMoving) {
// If it's the first slide, set as the last slide, else -1
if (slide === 0) {
slide = (totalItems - 1);
} else {
slide--;
}
}
return slide;
}
We can now combine the two moveCarouselTo function parameters together into the one carousel object.
carousel.slide = nextSlide(carousel);
// moveCarouselTo(carousel.slide, carousel.isMoving);
moveCarouselTo(carousel);
//...
carousel.slide = prevSlide(carousel);
moveCarouselTo(carousel);
//...
// function moveCarouselTo(slide, isMoving) {
function moveCarouselTo({slide, isMoving}) {
Use a guard clause
I also noticed in the nextSlide and prevSlide functions an easy improvement. Those functions are quite nested with several if statements. We can improve on that by exiting the function early, using what’s called a guard clause.
function nextSlide({slide, isMoving}) {
// if (!isMoving) {
if (isMoving) {
return slide;
}
//...
return slide;
}
function prevSlide({slide, isMoving}) {
// if (!isMoving) {
if (isMoving) {
return slide;
}
//...
return slide;
}
I also see that we can add a similar guard clause to the moveCarouselTo function as well.
function moveCarouselTo({slide, isMoving}) {
if(isMoving) {
return;
}
//...
}
Those guard clauses don’t need to be in the nextSlide, prevSlide, and moveCarouselTo functions. Instead they can be removed from there and put only in the event handlers.
function moveNextHandler(evt) {
if(carousel.isMoving) {
return;
}
carousel.slide = nextSlide(carousel);
moveCarouselTo(carousel);
}
function movePrevHandler(evt) {
if(carousel.isMoving) {
return;
}
carousel.slide = prevSlide(carousel);
moveCarouselTo(carousel);
}
//...
function moveCarouselTo({slide, isMoving}) {
// if(isMoving) {
// return;
// }
...
}
function nextSlide({slide, isMoving}) {
// if (isMoving) {
// return slide;
// }
...
}
function prevSlide({slide, isMoving}) {
// if (isMoving) {
// return slide;
// }
...
}
Examine remaining let variables
The last thing that we had on the TODO list is to investigate any remaining let
statements.
let newPrevious = slide - 1;
let newNext = slide + 1;
let oldPrevious = slide - 2;
let oldNext = slide + 2;
We can instead use some simple objects called prev and next to store that information.
const prev = {
new: slide - 1,
old: slide - 2
};
const next = {
new: slide + 1,
old: slide + 2
};
We can now rename things to move them over to the prev and next objects.
// if (newPrevious <= 0) {
if (prev.new <= 0) {
// oldPrevious = totalItems - 1;
prev.old = totalItems - 1;
// } else if (newNext >= totalItems - 1) {
} else if (next.new >= totalItems - 1) {
// oldNext = 0;
next.old = 0;
}
if (slide === 0) {
// newPrevious = totalItems - 1;
prev.new = totalItems - 1;
// oldPrevious = totalItems - 2;
prev.old = totalItems - 2;
// oldNext = slide + 1;
next.old = slide + 1;
} else if (slide === totalItems - 1) {
// newPrevious = slide - 1;
prev.new = slide - 1;
// newNext = 0;
next.new = 0;
// oldNext = 1;
next.old = 1;
}
// Based on the current slide, reset to default classes.
// items[oldPrevious].className = itemClassName;
items[prev.old].className = itemClassName;
// items[oldNext].className = itemClassName;
items[next.old].className = itemClassName;
// Add the new classes
// items[newPrevious].className = itemClassName + " prev";
items[prev.new].className = itemClassName + " prev";
// items[slide].className = itemClassName + " active";
items[slide].className = itemClassName + " active";
// items[newNext].className = itemClassName + " next";
items[next.new].className = itemClassName + " next";
Those last remaining let statements are no longer being used, and so can be removed.
// let newPrevious = slide - 1;
// let newNext = slide + 1;
// let oldPrevious = slide - 2;
// let oldNext = slide + 2;
Summary
This has been a somewhat in-depth look at using const and let instead of var, and removing the need for let. It can result in refactoring several changes to the code, but almost all of the time those changes result in improvements to the code and its structure.
Here’s a forked version of the codePen code that has all of the above improvements made to it. I also used the format javascript
option and JSLint to help make the code consistent too.