🛠Carousel: First refactor
It can be quite complicated to refactor the carousel since there are many moving parts. We’ll go through them together in a few lessons.
We’ll go from top to bottom to make things easier to explain.
Setting the position of all slides
We used this code to set the initial position of our slides:
const slideWidth = slides[0].getBoundingClientRect().width
slides.forEach((slide, index) => {
slide.style.left = slideWidth * index + 'px'
})
This code is imperative. We had to read through the code to understand what it does. If we put this code into a function like setSlidePositions
, we will know what the code does without even looking at it.
const setSlidePositions = _ => {
const slideWidth = slides[0].getBoundingClientRect().width
slides.forEach((slide, index) => {
slide.style.left = slideWidth * index + 'px'
})
}
Using the setSlidePositions
:
setSlidePositions()
Code for the nextButton
's event listener is quite complicated. We did five things here:
- Gets
currentSlide
, nextSlide
and destination
- Shows
nextSlide
by changing the transform
property
- Shows
previousButton
- Hides
nextButton
if next slide is the last slide
- Highlights the next dot
nextButton.addEventListener('click', event => {
const currentSlide = contents.querySelector('.is-selected')
const nextSlide = currentSlide.nextElementSibling
const destination = getComputedStyle(nextSlide).left
// Shows next slide
contents.style.transform = `translateX(-${destination})`
currentSlide.classList.remove('is-selected')
nextSlide.classList.add('is-selected')
// Shows previous button
previousButton.removeAttribute('hidden')
// Hides next button
if (!nextSlide.nextElementSibling) {
nextButton.setAttribute('hidden', true)
}
// Highlight dot
const currentDot = dotsContainer.querySelector('.is-selected')
const nextDot = currentDot.nextElementSibling
currentDot.classList.remove('is-selected')
nextDot.classList.add('is-selected')
})
The code for previousButton
's event listener is similar. We did the same five things:
- Gets
currentSlide
, previousSlide
and destination
- Shows
previousSlide
by changing the transform
property
- Shows
nextButton
- Hides
previousButton
if previous slide is the first slide
- Highlights the previous dot
previousButton.addEventListener('click', event => {
const currentSlide = contents.querySelector('.is-selected')
const previousSlide = currentSlide.previousElementSibling
const destination = getComputedStyle(previousSlide).left
// Shows previous slide
contents.style.transform = `translateX(-${destination})`
currentSlide.classList.remove('is-selected')
previousSlide.classList.add('is-selected')
// Shows next button
nextButton.removeAttribute('hidden')
// Hides previous button
if (!previousSlide.previousElementSibling) {
previousButton.setAttribute('hidden', true)
}
// Highlight dot
const currentDot = dotsContainer.querySelector('.is-selected')
const previousDot = currentDot.previousElementSibling
currentDot.classList.remove('is-selected')
previousDot.classList.add('is-selected')
})
There are two parts that are similar in these two sets of code:
- The part for switching slides
- The part for highlighting the dot
Refactoring the part for switching slides
We wrote this code to switch to the next slide:
// Shows next slide
contents.style.transform = `translateX(-${destination})`
currentSlide.classList.remove('is-selected')
nextSlide.classList.add('is-selected')
And we wrote this code to switch to the previous slide:
// Shows previous slide
contents.style.transform = `translateX(-${destination})`
currentSlide.classList.remove('is-selected')
previousSlide.classList.add('is-selected')
These two sets of code are similar. We can group them into a function called switchSlide
.
const switchSlide = _ => {
// ...
}
The easiest way to build switchSlide
is first to copy/paste the code we need into the function.
const switchSlide = _ => {
// Shows next slide
contents.style.transform = `translateX(-${destination})`
currentSlide.classList.remove('is-selected')
nextSlide.classList.add('is-selected')
// Shows previous slide
contents.style.transform = `translateX(-${destination})`
currentSlide.classList.remove('is-selected')
previousSlide.classList.add('is-selected')
}
Here, we know we need three things:
- The
currentSlide
- The slide we want to move to (either
nextSlide
or previousSlide
). We’ll call this targetSlide
.
- The
destination
(the left
property of the slide we want to move to).
We can pass these properties into the function as arguments:
const switchSlide = (currentSlide, targetSlide, destination) => {
contents.style.transform = `translateX(-${destination})`
currentSlide.classList.remove('is-selected')
targetSlide.classList.add('is-selected')
}
If you noticed, we can get destination
from targetSlide
. This means we can pass in one less variable. We’ll get destination
within switchSlide
.
const switchSlide = (currentSlide, targetSlide) => {
const destination = getComputedStyle(targetSlide).left
contents.style.transform = `translateX(-${destination})`
currentSlide.classList.remove('is-selected')
targetSlide.classList.add('is-selected')
}
Using switchSlide
:
nextButton.addEventListener('click', event => {
const currentSlide = contents.querySelector('.is-selected')
const nextSlide = currentSlide.nextElementSibling
switchSlide(currentSlide, nextSlide)
// ...
})
previousButton.addEventListener('click', event => {
const currentSlide = contents.querySelector('.is-selected')
const previousSlide = currentSlide.previousElementSibling
switchSlide(currentSlide, previousSlide)
// ...
})
Highlighting dots
Here’s the code we used to highlight the next dot:
nextButton.addEventListener('click', event => {
// ...
// Highlight dot
const currentDot = dotsContainer.querySelector('.is-selected')
const nextDot = currentDot.nextElementSibling
currentDot.classList.remove('is-selected')
nextDot.classList.add('is-selected')
})
And the code to highlight the previous dot:
previousButton.addEventListener('click', event => {
// ...
// Highlight dot
const currentDot = dotsContainer.querySelector('.is-selected')
const previousDot = currentDot.previousElementSibling
currentDot.classList.remove('is-selected')
previousDot.classList.add('is-selected')
})
Again, these two sets of code are similar. We can use the same technique as switchSlides
to build a function for them. Let’s call this function highlightDot
.
const highlightDot = _ => {
// ...
}
First, we copy/paste everything we need into hightlightDot
.
const highlightDot = _ => {
// Highlight next dot
const currentDot = dotsContainer.querySelector('.is-selected')
const nextDot = currentDot.nextElementSibling
currentDot.classList.remove('is-selected')
nextDot.classList.add('is-selected')
// Highlight previous Dot
const currentDot = dotsContainer.querySelector('.is-selected')
const previousDot = currentDot.previousElementSibling
currentDot.classList.remove('is-selected')
previousDot.classList.add('is-selected')
}
We can see we need two variables:
- The
currentDot
- The
targetDot
(either previousDot
or nextDot
)
We can pass these two variables into hightlightDot
.
const highlightDot = (currentDot, targetDot) => {
currentDot.classList.remove('is-selected')
targetDot.classList.add('is-selected')
}
Using highlightDot
:
nextButton.addEventListener('click', event => {
// ...
const currentDot = dotsContainer.querySelector('.is-selected')
const nextDot = currentDot.nextElementSibling
highlightDot(currentDot, nextDot)
// ...
})
previousButton.addEventListener('click', event => {
// ...
const currentDot = dotsContainer.querySelector('.is-selected')
const previousDot = currentDot.previousElementSibling
highlightDot(currentDot, previousDot)
})
What’s left from nextButton
and previousButton
event handlers are the part where we show/hide previous and next buttons.
Here’s the code we wrote for the showing/hiding the previous and next buttons:
nextButton.addEventListener('click', event => {
// ...
// Shows previous button
previousButton.removeAttribute('hidden')
// Hides next button
if (!nextSlide.nextElementSibling) {
nextButton.setAttribute('hidden', true)
}
})
previousButton.addEventListener('click', event => {
// ...
// Shows next button
nextButton.removeAttribute('hidden')
// Hides previous button
if (!previousSlide.previousElementSibling) {
previousButton.setAttribute('hidden', true)
}
})
It seems like there are no commonalities between these two sets of code. We can’t refactor them at this point.
You’ll start to see the commonalities between these two sets of code when we refactor the dots part in the next lesson.