Skip to content

Carousel - first refactor

Mit diesem Code haben wir die initiale Position der Slides gesetzt:

const slideWidth = slides[0].getBoundingClientRect().width
slides.forEach((slide, index) => {
slide.style.left = slideWidth * index + 'px'
})

Dieser Code ist nicht deklarativ, sondern imperativ. Wir müssen ihn mühsam durchlesen, um zu verstehen, was er tut. Wenn wir diesen in eine Funktion auslagern (z.B. setSlidePositions), dann versteht man sofort, was der Code macht, ohne ihn durchzulesen. slides muss nicht als Argument übergeben werden, weil es im global scope erreichbar ist.

function setSlidePositions() {
const slideWidth = slides[0].getBoundingClientRect().width
slides.forEach((slide, index) => {
slide.style.left = slideWidth * index + 'px'
})
}

Verwendung von setSlidePositions:

setSlidePositions()

Im Code für den nextButton’s EventListener haben wir fünf verschiedene Aufgaben erledigt:

  1. current/nextSlide identifiziert, Positionierung ermittelt
  2. den nächsten Slide angezeigt
  3. den Zurück-Button eingeblendet (weil es einen vorhergehenden Slide gibt)
  4. den Vorwärts-Button versteckt, wenn wir beim letzten Slide sind
  5. den aktuellen Dot hervorgehoben (mit der Zuweisung von is-selected)

Die entsprechenden Schritte gelten genauso für den previousButton.

Zwei Teile sind gleich:

  1. Der Teil zum wechseln der Slides
  2. Die Hervorhebung für den Dot

Hier noch einmal die beiden Code-Teile in der Übersicht:

nextButton.addEventListener('click', e => {
// ...
// Show next slide
contents.style.transform = `translateX(-${destination})`
currentSlide.classList.remove('is-selected')
nextSlide.classList.add('is-selected')
})
previousButton.addEventListener('click', e => {
// ...
// Show previous slide
contents.style.transform = `translateX(-${destination})`
currentSlide.classList.remove('is-selected')
previousSlide.classList.add('is-selected')
})

Der einzige Unterschied besteht darin, dass is-selected einmal dem nächsten und einmal dem vorhergehenden Slide zugewisen wird. Das lässt sich in eine Funktion switchSlide auslagern. Zuerst kopieren wir mal beide Teile in die Funktion:

function switchSlide() {
// Show next slide
contents.style.transform = `translateX(-${destination})`
currentSlide.classList.remove('is-selected')
nextSlide.classList.add('is-selected')
// Show previous slide
contents.style.transform = `translateX(-${destination})`
currentSlide.classList.remove('is-selected')
previousSlide.classList.add('is-selected')
}

Wir brauchen hier drei Dinge:

  • den currentSlide
  • next/previousSlide - diesen nennen wir targetSlide
  • destination, also den Wert, um den die Slides verschoben werden sollen

Diese drei Eigenschaften können wir der Funktion als Argumente mitgeben:

function switchSlide(currentSlide, targetSlide, destination) {
contents.style.transform = `translateX(-${destination})`
currentSlide.classList.remove('is-selected')
targetSlide.classList.add('is-selected')
}

destination erhalten wir aus dem next/previousSlide, also unserem targetSlide:

previousButton.addEventListener('click', e => {
// ...
const destination = getComputedStyle(previousSlide).left
})
nextButton.addEventListener('click', e => {
// ...
const destination = getComputedStyle(nextSlide).left
})

Daher nehmen wir diese Variablendeklaration aus den EventListenern raus und bringen sie in der Funktion unter:

function switchSlide(currentSlide, targetSlide) {
const destination = getComputedStyle(targetSlide).left
contents.style.transform = `translateX(-${destination})`
currentSlide.classList.remove('is-selected')
targetSlide.classList.add('is-selected')
}

Hier noch einmal die beiden Code-Teile in der Übersicht:

nextButton.addEventListener('click', e => {
// ...
// Highlight dot
const currentDot = dotsContainer.querySelector('.is-selected')
const nextDot = currentDot.nextElementSibling
currentDot.classList.remove('is-selected')
nextDot.classList.add('is-selected')
})
previousButton.addEventListener('click', e => {
// ...
// Highlight dot
const currentDot = dotsContainer.querySelector('.is-selected')
const previousDot = currentDot.previousElementSibling
currentDot.classList.remove('is-selected')
previousDot.classList.add('is-selected')
})

Auch dafür können wir eine Funktion schreiben. Nennen wir sie highlightDot. Zuerst kopieren wir wieder beide Teile rein:

function 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 previousDot
const currentDot = dotsContainer.querySelector('.is-selected')
const previousDot = currentDot.previousElementSibling
currentDot.classList.remove('is-selected')
previousDot.classList.add('is-selected')
}

Wir sehen, dass wir zwei Variable brauchen: currentDot und next/previousDot. Der wird wieder zum targetDot. Wir übergeben diese beiden Variable als Argumente an die Funktion:

function highlightDot(currentDot, targetDot) {
currentDot.classList.remove('is-selected')
targetDot.classList.add('is-selected')
}

Verwendung:

nextButton.addEventListener('click', e => {
// ...
const currentDot = dotsContainer.querySelector('is-selected')
const nextDot = currentDot.nextElementSibling
highlightDot(currentDot, nextDot)
})
previousButton.addEventListener('click', e => {
// ...
const currentDot = dotsContainer.querySelector('is-selected')
const previousDot = currentDot.previousElementSibling
highlightDot(currentDot, previousDot)
})