Skip to content

Creating dots with JavaScript

Da sich die Anzahl der Slides ändern kann, muss sich die Anzahl der Dots ebenfalls ändern. Diese ist im Moment hardcoded im HTML, was nicht besonders effizient ist. Wir wollen daher den gesamten Dot-Navigationsbereich mit JavaScript erstellen.

Zuerst entfernen wir den Bereich aus dem HTML:

<div class="carousel-dots">
<button class="carousel-dot is-selected"></button>
<button class="carousel-dot"></button>
<button class="carousel-dot"></button>
</div>

und diesen aus dem JavaScript:

const dotsContainer = carousel.querySelector('.carousel-dots');
const dots = [...carousel.querySelectorAll('.carousel-dot')];

Als erstes erstellen wir eine Funktion createDots. Diese soll das HTML, das wir oben rausgelöscht haben, erzeugen. Dazu verwenden wir die Methode createElement() und classList.add:

function createDots() {
const dotsContainer = document.createElement('div');
dotsContainer.classList.add('carousel-dots');
}

Um die Buttons zu erzeugen, loopen wir über die Slides. So erhalten wir pro Slide einen Button:

function createDots() {
// ...
slides.forEach(slide => {
const dot = document.createElement('button');
dot.classList.add('carousel-dot');
});
}

Nun fügen wir die Buttons mit der Methode appendChild() dem zuvor erstellten dotsContainer hinzu:

function createDots() {
// ...
slides.forEach(slide => {
const dot = document.createElement('button');
dot.classList.add('carousel-dot');
dotsContainer.appendChild(dot);
});
}

Als nächstes brauchen wir noch die Klasse is-selected auf dem ersten Dot. Dafür gibt es zwei Möglichkeiten:

  • Wir können den Index verwenden, also dot[0]
  • oder prüfen, ob der Slide die Klasse is-selected enthält.

Beide Methoden funktionieren, aber die zweite ist robuster, weil damit der Slider auch beim zweiten Slide beginnen kann.

function createDots() {
// ...
slides.forEach(slide => {
const dot = document.createElement('button');
dot.classList.add('carousel-dot');
if (slide.classList.contains('is-selected')) {
dot.classList.add('is-selected');
}
dotsContainer.appendChild(dot);
});
}

Zuletzt geben wir dotsContainer aus der Funktion zurück. Somit können wir dotsContainer nutzen wo wir wollen.

function createDots() {
// ...
return dotsContainer;
}

Um den DotContainer mit den Dots zu erstellen, müssen wir

  • die Variable dotsContainer deklarieren und die Funktion aufrufen
  • die Variable dots deklarieren und
  • den neu erstellten Container ins DOM einfügen
// declare the variables
const slides = [...carousel.querySelectorAll('.carousel-slide')];
const dotsContainer = createDots(slides);
const dots = [...dotsContainer.children];
// declare the functions
function createDots(slides);
// ...
// add the dotsContainer and the dots to the DOM
setSlidePositions();
carousel.appendChild(dotsContainer);

Unsere Funktion createDots() benötigt die Slides. Wir müssen sicherstellen, dass die Slides vorhanden sind, bevor wir createDots() aufrufen. Sie muss aber vor der Deklaration der Variable für den DotContainer stehen. Das ergibt eine ungute Mischung: zuerst Variablendeklarationen, dann eine Funktion, dann weitere Variablen. Das wollen wir vermeiden.

Im Moment funktioniert das Skript, weil slides vor der Verwendung von createDots() deklariert wurde. Wir sagen: slides ist im lexical scope`.

Wenn wir slides und createDots() vertauschen, funktioniert das Skript nicht mehr. Wenn wir externe Variable verwenden, wird Code fragil. Wenn wir der Funktion createDots() slides als Argument mitgeben, sehen wir auf einen Blick, dass die Deklaration von dotsContainer nach der Deklaration von slides kommen muss.

We’ve been relying on external variables

Section titled “We’ve been relying on external variables”

Bei jeder Funktion im Carousel haben wir uns auf externe Variable (und somit auf den lexical scope) verlassen. Das hat funktioniert, weil alle Variablen vorab deklariert wurden.

Als wir (kurzzeitig) die Funktion createDots() an den Anfang unseres JavaScript-Files gebracht haben, haben wir die Funktion quasi manuell “gehoisted”. Indem wir eine normale Funktion verwenden und der Funktion einen Parameter bzw. beim Aufruf das benötigte Argument mitgeben, passiert das automatisch. So können wir alle Variable und Funktionen sauber und getrennt voneinander im Code “aufheben”.

// Declaring all variables
// ...
const slides = [...carousel.querySelectorAll('.carousel-slide')];
const dotsContainer = createDots();
const dots = [...dotsContainer.children];
// Declaring all functions
function createDots(slides) {
// ...
}
// ...

Nicht elegant, weil keine Funktion, funzt aber trotzdem:

const dotList = slides.map(slide => `<button class="carousel-dot"></button>`).join('');
dotsContainer.innerHTML = dotList;
const dots = [...carousel.querySelectorAll('.carousel-dot')];
const firstDot = dots[0];
firstDot.classList.add('is-selected');