🛠 Carousel: First refactor

Hey ,

I'm thrilled to help you learn JavaScript. Unfortunately, you've landed on a page where you cannot access with your current purchase.

Please upgrade (use this link) access this content.

I'm super eager to help you learn more!

🛠 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()

The nextButton event listener

Code for the nextButton's event listener is quite complicated. We did five things here:

  1. Gets currentSlide, nextSlide and destination
  2. Shows nextSlide by changing the transform property
  3. Shows previousButton
  4. Hides nextButton if next slide is the last slide
  5. 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:

  1. Gets currentSlide, previousSlide and destination
  2. Shows previousSlide by changing the transform property
  3. Shows nextButton
  4. Hides previousButton if previous slide is the first slide
  5. 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:

  1. The part for switching slides
  2. 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:

  1. The currentSlide
  2. The slide we want to move to (either nextSlide or previousSlide). We’ll call this targetSlide.
  3. 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:

  1. The currentDot
  2. 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.

Showing/hiding 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.