Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

đŸ›Žïž Push the Button

🎯 Ziele

Unsere Buttons sehen zwar aus wie Buttons, aber sie machen noch nichts. Wir wollen folgende Funktionen erreichen:

  1. Artikel Like Button - mit Unlike und ZĂ€hler
  2. Autor:in Folgen Button - mit Unfollow
  3. Thema Folgen Button - mit Unfollow

Frontend-Focus: In dieser Umsetzung konzentrieren wir uns auf Frontend-Logik. Daten werden nur im Frontend gespeichert - bei einem Page-Reload werden alle Status zurĂŒckgesetzt.


Skript Setup

Button-Script erstellen

  1. Neue Datei erstellen: buttons.js im javascript Ordner
  2. Import in main.js hinzufĂŒgen
  3. Test mit Console-Output (siehe Kapitel "Table Auto Design")

🐛 Debugging-Optionen

FĂŒr lokales Debugging siehe:


Event Listener verstehen

Events Grundlagen

Ein Event ist eine Nachricht wenn eine Aktion stattfindet - wie "Button XY wurde gedrĂŒckt". Der Browser kennt Standard-Reaktionen auf Events (z.B. Checkbox an/aus).

Mehr dazu: MDN Event.preventDefault

Events finden

Öffne fĂŒr mehr Infos

Herausforderung: Nicht jedes Element bietet die selben Events an:

  • Button kann geklickt werden
  • Textfeld kann beschrieben werden
  • Maus kann bewegt werden

Aber wie finde ich heraus, welche Events verfĂŒgbar sind?

MDN-Dokumentation durchsuchen

  1. Button-spezifische API: HTMLButtonElement
  2. Instance Methods: HTMLButtonElement#instance_methods
  3. Problem: Der "click" Event ist hier nicht direkt zu finden!

Vererbungskette verstehen

Wichtiger Hinweis: Button erbt Methoden vom Eltern-Element:

"inherits methods from its parent, HTMLElement"

Lösungsweg: Durch die Vererbungskette navigieren:

  • HTMLButtonElement → HTMLElement → Element
  • Ziel: Element API - hier finden sich die meisten Events!

Praktische Tipps

MethodeBeschreibungEffektivitÀt
DokumentationMDN systematisch durchsuchenVollstÀndig aber zeitaufwÀndig
Trial & ErrorEvents ausprobieren und testenSchnell fĂŒr bekannte Events
IDE-SupportAutocomplete und IntelliSense nutzenSehr praktisch wÀhrend Entwicklung

Praxis-Tipp: Die Devise "trial and error" ist oft effizienter als stundenlanges Dokumentation-Studium!

Event Listener erstellen

Schritt 1: Alle Buttons finden und Event Listener registrieren

document.querySelectorAll("button").forEach((button) => {
  // FĂŒr jeden Button einen Click-Listener erstellen
});

Event Listener hinzufĂŒgen: MDN addEventListener

button.addEventListener("click", () => {
  // Handle button click
});

Der erste Parameter ist der Event Type, der zweite (nach dem ,) nimmt eine Funktion entgegen, welche ausgefĂŒhrt werden soll, wenn der Listener greift. Nun wird es etwas kompliziert, wenn du dies im Detail verstehen möchtest
 Lies dazu JavaScript und Callbacks oder ĂŒbernimmt einfach mal nachfolgende Code Snippets.

Erster Test

document.querySelectorAll("button").forEach((button) => {
  button.addEventListener("click", () => {
    // Handle button click
    alert(`Button "${button.textContent.trim()}" wurde geklickt!`);
  });
});

Teste im Browser - bei jedem Button-Klick sollte ein Alert erscheinen!


HTML mit Data-Attributen erweitern

Button-Unterscheidung

Wir haben 3 verschiedene Button-Typen. FĂŒr bessere JavaScript-Kontrolle erweitern wir das HTML:

Data-Attribute hinzufĂŒgen:

  • data-button-state: "active" oder "inactive"
  • data-button: Button-Name zur Identifikation

HTML-Updates

Like Button

<button
  data-button-state="inactive"
  data-button="like_article"
  class="primary mb-s font-small align-items-center text-center">

Autor:in folgen

<button
  data-button-state="inactive"
  data-button="follow_author"
  class="primary">

Thema folgen

<button
  data-button-state="inactive"
  data-button="follow_topic"
  class="primary">

Spezifische Button-Auswahl

document.querySelectorAll("button[data-button]").forEach((button) => {
  // Nur Buttons mit data-button Attribut
});

Button States Management

⚡ Switch-Statement fĂŒr Button-Typen

Siehe auch JavaScript Switch Statement

document.querySelectorAll("button[data-button]").forEach((button) => {
  button.addEventListener("click", () => {
    switch (button.dataset.button) {
      case "like_article":
        break;
      case "follow_author":
        break;
      case "follow_topic":
        break;
      default:
        console.warn(`Unbekannter Button-Typ: ${button.dataset.button}`);
    }
  });
});

Toggle-FunktionalitÀt

Im nĂ€chsten Schritt wollen wir ein einfaches "toggle" einbauen. Das ist der Begriff, wenn zwischen ZustĂ€nden gewechselt wird. Meistens zwischen 2 wie bei uns. Von inaktiv auf aktiv und zurĂŒck.

Toggle-Funktion erstellen:

Damit der Button tatsÀchlich "toggled" wollen wir die Funktion toggleButtonState so erweitern, dass sie den nun aktiven State ausliest und ihn "umdreht". ZusÀtzlich setzen bzw. entfernen wir den primary-button als CSS Klasse. So wird bereits optisch was passieren.

Lies also als erstes den state aus dem Button und speichere ihn als lokale Variable in der Funktion ab.

function toggleButtonState(button) {
  const currentState = button.dataset.buttonState;

Nun kannst du mit einem "if" unterscheiden was basierend auf dem aktuellen State passieren soll. Nebst der CSS Klasse setzen wir auch den State zurĂŒck.

function toggleButtonState(button) {
  const currentState = button.dataset.buttonState;
  
  if (currentState == "active") {
    button.classList.add("primary");
    button.dataset.buttonState = "inactive";
  } else {
    button.classList.remove("primary");
    button.dataset.buttonState = "active";
  }
}

Funktionsaufruf in Switch-Statement:

Nun muss die Funktion noch aufgerufen werden.

switch (button.dataset.button) {
  case "like_article":
    toggleButtonState(button);
    break;
  case "follow_author":
    toggleButtonState(button);
    break;
  case "follow_topic":
    toggleButtonState(button);
    break;
}

CSS-Klassen automatisch togglen

Mit classList.toggle wird das setzen/entfernen der CSS Klasse noch einfacher:

function toggleButtonState(button) {
  const currentState = button.dataset.buttonState;
  button.classList.toggle("primary");
  
  if (currentState == "active") {
    button.dataset.buttonState = "inactive";
  } else {
    button.dataset.buttonState = "active";
  }
}

Button-Texte dynamisch Àndern

Text-Parameter hinzufĂŒgen

function toggleButtonState(button, activationText, inactivatingText) {
  const currentState = button.dataset.buttonState;
  button.classList.toggle("primary");

  if (currentState == "active") {
    button.textContent = activationText;
    button.dataset.buttonState = "inactive";
  } else {
    button.textContent = inactivatingText;
    button.dataset.buttonState = "active";
  }
}

ErklÀrungen

  • mit .textContent erhalten wir direkt den Text ohne inneres HTML
  • activationText und inactivatingText sind zwei neue Funktions Parameter
  • Die Texte mĂŒssen ĂŒbergeben werden, damit sie was tun

Text-Konstanten definieren

Diese werden am Anfang des Files definiert und erlauben einfaches lesen der Texte.

const likePageText = "Dieser Artikel gefÀllt mir!";
const unlikePageText = "Dieser Artikel gefÀllt mir nicht mehr";
const followAuthorText = "Autor:in folgen";
const unfollowAuthorText = "Autor:in nicht mehr folgen";
const followTopicText = "Thema folgen";
const unfollowTopicText = "Thema entfolgen";

Switch-Statement mit Texten

Damit die Texte stimmen, ĂŒbergibst du sie aus der Switch Funktion an die Toggle Funktion.

switch (button.dataset.button) {
  case "like_article":
    toggleButtonState(button, likePageText, unlikePageText);
    break;
  case "follow_author":
    toggleButtonState(button, followAuthorText, unfollowAuthorText);
    break;
  case "follow_topic":
    toggleButtonState(button, followTopicText, unfollowTopicText);
    break;
}

Like Button erweitern

Like Counter implementieren

Der Like Button braucht zusÀtzliche Features:

  • ZĂ€hler fĂŒr Likes
  • Icon mit Herz/gebrochenem Herz (Kosmetik)

HTML fĂŒr Counter markieren

Zahl mit span und ID markieren:

Den counter legen wir lokal im JavaScript an, mit dem Bewusstsein, dass die Daten spĂ€ter von einem Server kommen sollten. Dazu mĂŒssen wir zudem im HTML die Stelle markieren, wo der Counter steht.

Gehe zur Stelle im HTML wo steht "59 Personen gefĂ€llt dieser Artikel". Wir wollen nur die Zahl steuern können. DafĂŒr ist ein <span> Element perfekt geeignet, da es keine Zeile erstellt. FĂŒge ein span Element um die Zahl ein und vergib die id "data-like-counter".

<span id="data-like-counter">59</span> Personen gefÀllt dieser Artikel

💡 Wichtig: IDs dĂŒrfen nur einmal pro HTML existieren!

Like-spezifische Toggle-Funktion

Der Like Button wird nun eine Sonderbehandlung brauchen. Lege eine neue Function an im JavaScript und nenne sie toggleLikeButtonState. Als Parameter brauchen wir den Button.

function toggleLikeButtonState(button) {
  const currentState = button.dataset.buttonState;
  const likeCounter = document.querySelector("#data-like-counter");
  // Alternative: document.getElementById("data-like-counter");
  
  let currentCount = parseInt(likeCounter.textContent);
  
  if (currentState == "inactive") {
    currentCount++;
  } else {
    currentCount--;
  }
  
  likeCounter.textContent = currentCount;
}

ErklÀrungen

  • currentCountdient als Zwischenspeicher des aktuellen Count
  • parseIntkonvertiert den Text zu einer Zahl, damit das Rechnen sauber klappt
  • button.dataset.buttonStateholt den Wert des Data Attributes mit Namen button-state
  • der currentState dient fĂŒr die Unterscheidung, ob plus oder minus gezĂ€hlt wird
  • mit likeCounter.textContent = wird der neue Wert gesetzt

Integration mit bestehender Toggle-Logik

Handelt es sich um den Like Button, soll die neue Logik aufgerufen werden.

case "like_article":
  toggleLikeButtonState(button, likePageText, unlikePageText);
  break;

Text und Aussehen korrigieren

Wenn du jetzt ausprobierst, was passiert, wirst du bemerken, dass zwar der ZĂ€hler funktioniert, dafĂŒr aber der Text und Aussehen nicht mehr stimmen. Das gute ist, die Logik haben wir ja bereits im "toggleButtonState" geschrieben und können sie einfach nochmals aufrufen, bevor wir den Counter setzen.

function toggleLikeButtonState(button) {
  const currentState = button.dataset.buttonState;
  const likeCounter = document.querySelector("#data-like-counter");
  // Alternative: document.getElementById("data-like-counter");
  
  let currentCount = parseInt(likeCounter.textContent);
  
  // bestehende Logik wiederverwenden
  toggleButtonState(button, likePageText, unlikePageText);

  if (currentState == "inactive") {
    currentCount++;
  } else {
    currentCount--;
  }
  
  likeCounter.textContent = currentCount;
}

Refactoring

An der Lösung ist noch etwas unsauber, dass sich die Methode toggleLikeButtonState ums direkte setzten der Texte kĂŒmmert. Wir möchten auch hier die Texte als Parameter entgegen nehmen, auch wenn wir hier grundsĂ€tzlich "wissen" um welche Texte es sich handelt. Lagerst du die Verantwortung jedoch an den Aufrufer aus, dann ist die Methode "unabhĂ€ngig" und könnte einfacher umgezogen werden. Erweitere also die Methode genau gleich wie toggleButtonState um die zwei Parameter, ĂŒbergib dieses an die toggleButtonState Methode und ĂŒbergib die Werte wieder bei der Switch Methode.

case "like_article":
        toggleLikeButtonState(button, likePageText, unlikePageText);
        break;

function toggleLikeButtonState(button, activationText, inactivatingText) {
  const currentState = button.dataset.buttonState;
  const likeCounter = document.getElementById("data-like-counter");
  let currentCount = parseInt(likeCounter.textContent);
  toggleButtonState(button, activationText, inactivatingText);

Icon-Integration

Das Icon wird aktuell gelöscht, sobald du den Button drĂŒckst. Eigentlich möchten wir auch das Icon korrekt setzen. DafĂŒr erstellen wir zuerst einmal das nötige HTML, dass wir dann einsetzen können. Erstelle am Anfang des Scripts je eine Variable fĂŒr das "filledHeart" und das "brokenHeart".

SVG-Icons vorbereiten

const filledHeart = document.createElement("span");
const brokenHeart = document.createElement("span");

filledHeart.innerHTML = `<svg
  fill="none" viewBox="0 0 16 16" width="16" height="16"
  class="svg-icon">
  <path fill="#fff" fill-rule="evenodd" 
    d="M13.971 3.029a3.53 3.53 0 0 0-4.983 0L8 4.018l-.988-.989a3.53 3.53 0 0 0-4.983 0 3.54 3.54 0 0 0 0 4.991L8 14l5.972-5.98a3.54 3.54 0 0 0 0-4.991" 
    clip-rule="evenodd"></path>
</svg>`;

brokenHeart.innerHTML = `<svg
  fill="none" viewBox="0 0 16 16" width="16" height="16"
  class="svg-icon">
  <path fill="#000" fill-rule="evenodd" 
    d="M13.971 3.029a3.53 3.53 0 0 0-4.983 0L8 4.018l-.988-.989a3.53 3.53 0 0 0-4.983 0 3.54 3.54 0 0 0 0 4.991L8 14l5.972-5.98a3.54 3.54 0 0 0 0-4.991" 
    clip-rule="evenodd"></path>
  <polyline points="6,4 8,8 7,10 9,12" stroke="#fff" stroke-width="1" fill="none"/>
</svg>`;

// Abstand zwischen Icon und Text
filledHeart.classList.add("mr-s");
brokenHeart.classList.add("mr-s");

ErklÀrungen

  • createElementerstellt ein HTML Element vom Typ, den du mitgibst
  • mit innerHtmlkann HTML angefĂŒgt werden
  • der SVG Code selbst ist eher kommpliziert. Im Prinzip gibt es die Linien vor, die gezeichnet werden

Icons in Toggle-Funktion integrieren

if (currentState == "inactive") {
  button.prepend(filledHeart);
  currentCount++;
} else {
  button.prepend(brokenHeart);
  currentCount--;
}

🎉 Ergebnis

Alle Buttons sind jetzt voll funktionsfÀhig:

✅ Like Button: Toggle mit Counter und Icon
✅ Follow Author: Toggle mit Text-Änderung
✅ Follow Topic: Toggle mit Text-Änderung

NĂ€chste Schritte

Im nĂ€chsten Kapitel verbinden wir die Buttons mit einem Backend fĂŒr persistente Datenspeicherung!