Zurück zur Hauptseite

JavaScripttutorial

JavaScript von Anfang an

Webseiten zum Leben erwecken – interaktiv, dynamisch und direkt im Browser

DE EN

Was ist JavaScript?

JavaScript ist die Programmiersprache des Webs. Während HTML die Struktur einer Seite beschreibt und CSS ihr Aussehen festlegt, ist JavaScript dafür zuständig, was passiert, wenn Nutzerinnen und Nutzer mit einer Seite interagieren. Ein Klick auf einen Button, eine Live-Suche, eine Animation, ein Pop-up-Fenster – all das wird durch JavaScript ermöglicht. JavaScript läuft direkt im Browser, ohne dass etwas installiert werden muss. Gleichzeitig kann es seit der Einführung von Node.js auch auf dem Server eingesetzt werden. Es ist die meistverwendete Programmiersprache der Welt und bildet die Grundlage für moderne Frameworks wie React, Vue und Angular.

So nutzt du die Inhalte

Dieses Tutorial ist so aufgebaut, dass du Schritt für Schritt vorgehst – von der Einrichtung bis zum fertigen Mini-Projekt. Lies jeden Abschnitt, tippe den Beispielcode selbst ab und löse die Übungsaufgaben. Aktives Mitmachen ist der Schlüssel zum Erfolg.

1. Lesen & Verstehen

Lies die Erklärungen aufmerksam. Wenn du einen Begriff nicht kennst, suche ihn kurz nach – das vertieft dein Verständnis und hilft langfristig.

2. Code abtippen

Tippe die Codebeispiele immer selbst ab, anstatt sie zu kopieren. Deine Finger lernen mit – und du entdeckst Tippfehler, bevor sie zum Problem werden.

3. Aufgaben lösen

Jedes Kapitel endet mit Übungsaufgaben in drei Schwierigkeitsstufen. Versuche erst selbst eine Lösung, bevor du nachschaust. Fehler sind normal und gehören dazu.

Vorarbeiten

Setup & die Browser-Konsole

Was du brauchst – und was du nicht brauchst. Das Beste an JavaScript ist: Du brauchst nichts installieren, um loszulegen. Jeder moderne Browser – Chrome, Firefox, Edge oder Safari – enthält einen vollständigen JavaScript-Interpreter. Öffne einfach die Entwicklertools mit der Taste F12 (oder auf dem Mac mit Cmd + Option + I), wechsle zum Reiter "Konsole" und du kannst sofort JavaScript eingeben und ausführen.

Die Browser-Konsole kennenlernen. Die Konsole ist dein wichtigstes Werkzeug beim Lernen und Debuggen. Hier kannst du einzelne JavaScript-Befehle ausprobieren, Fehler anzeigen lassen und mit console.log() Werte ausgeben. Tippe jetzt in der Konsole 2 + 3 ein und drücke Enter – du siehst sofort das Ergebnis 5. So funktioniert JavaScript: schnell, direkt, interaktiv.

// In der Browser-Konsole ausprobieren:
console.log("Hallo Welt!");
console.log(2 + 3);
console.log(typeof "JavaScript");

// Mathematik geht auch direkt:
5 * 8        // ergibt 40
Math.sqrt(16) // ergibt 4

JavaScript in eine HTML-Datei einbinden. Für echte Projekte schreibst du JavaScript-Code in Dateien. Es gibt zwei Wege: intern (direkt im HTML-Dokument mit einem <script>-Tag) und extern (in einer eigenen .js-Datei, die du verlinkst). Beide Varianten haben ihren Platz – für kleine Tests ist intern praktisch, für größere Projekte ist extern der Standard.

<!-- Intern: JavaScript direkt im HTML -->
<!DOCTYPE html>
<html lang="de">
<head>
  <meta charset="UTF-8" />
  <title>Meine erste JS-Seite</title>
</head>
<body>
  <h1>Hallo!</h1>

  <!-- Script am Ende des body-Tags einfügen -->
  <script>
    console.log("Seite geladen!");
  </script>
</body>
</html>

<!-- Extern: JavaScript in eigener Datei -->
<script src="script.js"></script>

Warum der Script-Tag ans Ende gehört. Das <script>-Tag sollte am Ende des <body>-Elements stehen, nicht im <head>. Der Grund: Der Browser liest HTML von oben nach unten. Steht das Script im Head, versucht JavaScript auf HTML-Elemente zuzugreifen, die noch gar nicht geladen sind – und es passieren Fehler. Am Ende des Body ist sichergestellt, dass die gesamte Seite bereits aufgebaut wurde.

VS Code einrichten. Für alle Aufgaben jenseits der Konsole empfiehlt sich ein guter Code-Editor. Visual Studio Code (kostenlos, von Microsoft) ist die Standardwahl. Installiere die Erweiterung "Prettier" für automatische Code-Formatierung und "ESLint" für Fehlererkennung. Mit der Erweiterung "Live Server" kannst du eine HTML-Datei speichern und sie wird sofort im Browser aktualisiert – ideal beim Lernen.

Übungsaufgaben
  1. Einfach: Öffne die Browser-Konsole (F12) und gib console.log("Mein Name ist " + "Max") ein. Ersetze "Max" durch deinen Namen.
  2. Mittel: Erstelle eine Datei index.html mit einem Script-Tag am Ende des Body. Gib darin drei verschiedene Werte mit console.log() aus: eine Zahl, einen Text und das Ergebnis einer Rechnung.
  3. Schwer: Erstelle eine externe Datei script.js und binde sie in deine HTML-Datei ein. Das Script soll console.log("Extern geladen!") ausgeben. Überprüfe in der Konsole, ob es funktioniert.

Jetzt geht es los!

Kapitel 1: Warum JavaScript?

Dein wichtigstes Werkzeug: die Browser-Konsole
JavaScript läuft im Browser. Um zu sehen, was dein Code macht, öffne die Entwicklertools mit der Taste F12 (oder Fn+F12 auf manchen Laptops) und wechsle zum Reiter „Konsole". Dort erscheinen alle console.log()-Ausgaben – und auch Fehlermeldungen. Halte dieses Fenster immer offen, während du JavaScript schreibst.

JavaScript – die Sprache, die das Web lebendig macht. Wenn du eine Webseite besuchst und ein Menü aufklappt, ein Bild mit der Maus verschoben werden kann, Suchergebnisse in Echtzeit erscheinen oder ein Formular sofort auf Fehler hinweist – dann ist JavaScript am Werk. Ohne JavaScript wären Webseiten statische Dokumente, vergleichbar mit gedruckten Broschüren. Mit JavaScript werden sie zu interaktiven Anwendungen.

Was JavaScript alles kann. JavaScript ist eine vielseitige Sprache, die im Laufe der Zeit weit über den Browser hinausgewachsen ist. Im Browser steuert JavaScript das DOM (Document Object Model), also die gesamte Struktur der Webseite. Es kann Elemente hinzufügen, entfernen, verändern und animieren. Es kommuniziert mit Servern (APIs), verarbeitet Benutzereingaben und speichert Daten lokal im Browser. Mit Node.js läuft JavaScript auch auf dem Server und kann Datenbanken ansprechen, Dateien lesen und schreiben und vollständige Backend-Systeme betreiben.

// Typische JavaScript-Anwendungen im Browser:

// 1. Text auf einer Seite ändern
document.getElementById("titel").textContent = "Hallo Welt!";

// 2. Auf einen Klick reagieren
button.addEventListener("click", function() {
  alert("Button wurde geklickt!");
});

// 3. Daten von einem Server laden (API)
fetch("https://api.example.com/daten")
  .then(response => response.json())
  .then(daten => console.log(daten));

Wo JavaScript eingesetzt wird. Fast jede große Webseite der Welt nutzt JavaScript. Google nutzt es für die sofortige Vorschau von Suchergebnissen. YouTube lädt neue Videos ohne Seitenneuladen. Facebook aktualisiert den Newsfeed in Echtzeit. Dropbox und Google Docs sind vollständige Anwendungen, die komplett im Browser laufen. Auch Spiele, interaktive Karten (Google Maps) und Datenvisualisierungen (Diagramme, Charts) werden mit JavaScript gebaut.

Der Unterschied zu HTML und CSS. Diese drei Technologien arbeiten zusammen, haben aber klar getrennte Aufgaben. HTML definiert den Inhalt und die Struktur: Überschriften, Absätze, Bilder, Links. CSS gestaltet das Aussehen: Farben, Abstände, Schriftarten, Layout. JavaScript fügt das Verhalten hinzu: Was passiert, wenn jemand klickt, tippt, scrollt oder eine Taste drückt? Die drei Sprachen ergänzen sich, und gutes Webentwickeln bedeutet, sie klar voneinander zu trennen.

// Vergleich: Was welche Sprache macht

// HTML: Struktur
// <button id="meinButton">Klick mich!</button>

// CSS: Aussehen
// button { background: blue; color: white; padding: 10px; }

// JavaScript: Verhalten
document.getElementById("meinButton").addEventListener("click", function() {
  console.log("Geklickt!");
  this.style.background = "green"; // Farbe ändert sich beim Klick
});

JavaScript-Versionen und ECMAScript. JavaScript wird ständig weiterentwickelt. Der offizielle Standard heißt ECMAScript (kurz: ES). Wichtige Versionen sind ES5 (2009, bis heute kompatibel), ES6/ES2015 (brachte viele moderne Features: let/const, Arrow Functions, Template Literals) und alle jährlichen Folgeversionen. In diesem Tutorial lernst du modernes JavaScript (ES6+), das alle aktuellen Browser unterstützen.

Übungsaufgaben
  1. Einfach: Besuche eine Webseite deiner Wahl und öffne die Entwicklertools (F12). Wechsle zum Reiter "Quellen" oder "Sources". Siehst du JavaScript-Dateien? Welche Dateinamen fallen dir auf?
  2. Mittel: Erkläre in eigenen Worten den Unterschied zwischen HTML, CSS und JavaScript. Schreibe je einen Satz für jede Technologie und gib ein konkretes Beispiel an, wofür sie zuständig ist.
  3. Schwer: Recherchiere, was Node.js ist und wie es sich von JavaScript im Browser unterscheidet. Welche bekannten Anwendungen nutzen Node.js auf dem Server? Halte deine Erkenntnisse stichwortartig fest.

Kapitel 2: Variablen & Datentypen

Was ist eine Variable? Eine Variable ist ein benannter Speicherplatz für einen Wert. Stell dir eine Variable wie eine beschriftete Schachtel vor: Du gibst der Schachtel einen Namen und legst einen Wert hinein. Später kannst du die Schachtel beim Namen nennen und ihren Inhalt abrufen oder verändern. In JavaScript gibt es drei Schlüsselwörter zum Erstellen von Variablen: var, let und const.

let, const und var – der Unterschied. let erstellt eine Variable, deren Wert sich verändern darf. const erstellt eine Konstante – der Wert kann nach der Zuweisung nicht mehr geändert werden. var ist das ältere Schlüsselwort und sollte in modernem Code vermieden werden, weil es ein unintuitives Verhalten beim Gültigkeitsbereich (Scope) hat. Die Faustregel lautet: Nutze immer const als Standard. Verwende let nur dann, wenn du weißt, dass sich der Wert ändern wird. Benutze var gar nicht.

// const: Wert bleibt immer gleich
const name = "Alice";
const PI = 3.14159;

// let: Wert kann sich ändern
let punktestand = 0;
punktestand = punktestand + 10; // jetzt 10
punktestand += 5;               // jetzt 15

// var: vermeiden! (veraltetes Verhalten)
var altesSchluesselwort = "bitte nicht mehr nutzen";

// Fehler bei const:
const unveraenderlich = 42;
// unveraenderlich = 99; // TypeError! Geht nicht.

Datentypen in JavaScript. JavaScript kennt verschiedene Typen von Werten. Die wichtigsten sind: string (Zeichenketten wie "Hallo"), number (Zahlen wie 42 oder 3.14 – keine Unterscheidung zwischen Ganzzahlen und Dezimalzahlen), boolean (wahr oder falsch: true oder false), null (bewusst kein Wert) und undefined (Variable existiert, hat aber noch keinen zugewiesenen Wert). JavaScript ist eine dynamisch typisierte Sprache – das bedeutet, eine Variable kann ihren Typ während der Laufzeit ändern.

// Die Grunddatentypen
const text = "JavaScript ist toll";   // string
const alter = 25;                     // number
const kommazahl = 3.14;               // number
const istWahr = true;                 // boolean
const istFalsch = false;              // boolean
const leer = null;                    // null (absichtlich kein Wert)
let nochNichtGesetzt;                 // undefined

// typeof gibt den Datentyp zurück
console.log(typeof text);        // "string"
console.log(typeof alter);       // "number"
console.log(typeof istWahr);     // "boolean"
console.log(typeof leer);        // "object" (historischer Bug in JS!)
console.log(typeof nochNichtGesetzt); // "undefined"

Template Literals – Strings elegant zusammensetzen. Mit Template Literals (Backtick-Strings) kannst du Variablen direkt in einen String einbetten, ohne sie mit + zusammenkleben zu müssen. Das macht den Code lesbarer. Du erkennst Template Literals am Backtick-Zeichen (`` ` ``) und an der ${...}-Syntax für Ausdrücke.

const vorname = "Max";
const nachname = "Mustermann";
const alter = 30;

// Alter Weg mit + (umständlich)
const begrüßung1 = "Hallo, " + vorname + " " + nachname + "! Du bist " + alter + " Jahre alt.";

// Moderner Weg mit Template Literals (empfohlen)
const begrüßung2 = `Hallo, ${vorname} ${nachname}! Du bist ${alter} Jahre alt.`;

// Ausdrücke im Template Literal
const ergebnis = `Nächstes Jahr bist du ${alter + 1} Jahre alt.`;
const groß = `Dein Name in Großbuchstaben: ${vorname.toUpperCase()}`;

console.log(begrüßung2);
console.log(ergebnis);

Typische Anfängerfehler. Ein häufiger Fehler ist es, null und undefined zu verwechseln. undefined bedeutet: "Diese Variable wurde noch nie einen Wert zugewiesen." null bedeutet: "Diese Variable hat absichtlich keinen Wert – das ist eine bewusste Entscheidung des Programmierers." Ein weiterer Fehler: const bei Objekten und Arrays verhindert nur die Neuzuweisung, aber nicht die Veränderung des Inhalts. Das kann zunächst verwirrend sein.

Übungsaufgaben
  1. Einfach: Erstelle drei Variablen: deinen Namen (const), dein Alter (let) und ob du gerne programmierst (const, boolean). Gib alle drei mit console.log() aus.
  2. Mittel: Baue einen Template-Literal-String, der folgendes ausgibt: "Ich heiße [Name], bin [Alter] Jahre alt und nächstes Jahr bin ich [Alter+1]." Nutze echte Variablen für alle drei Werte.
  3. Schwer: Experimentiere mit typeof: Was gibt typeof null zurück? Was gibt typeof undefined zurück? Was gibt typeof NaN zurück? (NaN = Not a Number, z.B. 0/0). Erkläre, warum diese Ergebnisse manchmal überraschend sind.

Kapitel 3: Operatoren & Vergleiche

Arithmetische Operatoren. JavaScript kann wie ein Taschenrechner verwendet werden. Die grundlegenden arithmetischen Operatoren sind: + (Addition), - (Subtraktion), * (Multiplikation), / (Division), % (Modulo – der Rest einer Division) und ** (Potenz, seit ES2016). Besonders nützlich ist das Modulo: Mit 7 % 2 prüfst du, ob eine Zahl gerade oder ungerade ist – das Ergebnis ist 1 (Rest bei Division durch 2), bei geraden Zahlen wäre es 0.

// Arithmetische Operatoren
console.log(10 + 3);   // 13
console.log(10 - 3);   // 7
console.log(10 * 3);   // 30
console.log(10 / 3);   // 3.3333...
console.log(10 % 3);   // 1  (Rest der Division: 10 = 3*3 + 1)
console.log(2 ** 8);   // 256 (2 hoch 8)

// Kurzschreibweisen
let x = 10;
x += 5;   // x = x + 5 → 15
x -= 3;   // x = x - 3 → 12
x *= 2;   // x = x * 2 → 24
x /= 4;   // x = x / 4 → 6
x++;      // x = x + 1 → 7 (Inkrement)
x--;      // x = x - 1 → 6 (Dekrement)

// Achtung: + mit Strings verknüpft!
console.log("5" + 3);  // "53" (String-Konkatenation!)
console.log(5 + 3);    // 8  (Zahlenaddition)

Vergleichsoperatoren – == vs. ===. Hier lauert einer der häufigsten Fehler für JavaScript-Einsteiger: der Unterschied zwischen doppeltem und dreifachem Gleichheitszeichen. == vergleicht nur den Wert, wandelt dabei aber den Typ automatisch um (Type Coercion). === vergleicht Wert und Typ – ohne Umwandlung. Die Faustregel: Nutze immer === (strikter Vergleich). Das Gleiche gilt für Ungleichheit: != vs. !==.

// Vergleichsoperatoren
console.log(5 == "5");   // true  (Typ wird umgewandelt – ACHTUNG!)
console.log(5 === "5");  // false (verschiedene Typen)
console.log(5 === 5);    // true  (gleicher Wert, gleicher Typ)

console.log(5 != "5");   // false (Type Coercion)
console.log(5 !== "5");  // true  (strikter Vergleich: verschiedene Typen)

// Größer / Kleiner
console.log(10 > 5);     // true
console.log(10 < 5);     // false
console.log(10 >= 10);   // true
console.log(10 <= 9);    // false

// Kuriose Beispiele mit ==
console.log(0 == false);   // true  (0 und false gelten als gleich)
console.log("" == false);  // true  (leerer String = false)
console.log(null == undefined); // true (mit ==)
console.log(null === undefined); // false (mit ===)

Logische Operatoren. Logische Operatoren verknüpfen Bedingungen miteinander. && (UND) ist nur dann wahr, wenn beide Seiten wahr sind. || (ODER) ist wahr, wenn mindestens eine Seite wahr ist. ! (NICHT) kehrt einen Wahrheitswert um: aus true wird false und umgekehrt. Diese Operatoren sind essenziell für Bedingungen und werden in nahezu jedem JavaScript-Programm verwendet.

// Logische Operatoren
const istErwachsen = true;
const hatFührerschein = false;

console.log(istErwachsen && hatFührerschein); // false (beide müssen true sein)
console.log(istErwachsen || hatFührerschein); // true (einer reicht)
console.log(!istErwachsen);                  // false (umgekehrt)
console.log(!hatFührerschein);               // true

// Kombination
const alter = 20;
const hatAusweis = true;
if (alter >= 18 && hatAusweis) {
  console.log("Einlass gewährt!");
}

Der ternäre Operator. Der ternäre Operator ist eine Kurzschreibweise für einfache if-else-Bedingungen in einer Zeile. Die Syntax lautet: Bedingung ? Wert_wenn_wahr : Wert_wenn_falsch. Er ist nützlich für kurze Fallunterscheidungen, sollte aber nicht für komplexe Logik verwendet werden, da er dann schnell unlesbar wird.

// Ternärer Operator
const alter = 20;

// Ohne ternären Operator:
let status;
if (alter >= 18) {
  status = "erwachsen";
} else {
  status = "minderjährig";
}

// Mit ternärem Operator (kürzer):
const status2 = alter >= 18 ? "erwachsen" : "minderjährig";

console.log(status2); // "erwachsen"

// Weitere Beispiele:
const note = 85;
const bewertung = note >= 90 ? "sehr gut" : note >= 75 ? "gut" : "befriedigend";
console.log(bewertung); // "gut"
Übungsaufgaben
  1. Einfach: Berechne mit JavaScript in der Konsole: Wie viele Minuten hat ein Jahr? (365 * 24 * 60). Wie viele Sekunden? Nutze Variablen für Tage, Stunden und Minuten.
  2. Mittel: Schreibe eine Variable zahl = 17 und prüfe mit dem Modulo-Operator (%), ob die Zahl gerade oder ungerade ist. Gib das Ergebnis als lesbaren Satz aus, z.B. "17 ist ungerade."
  3. Schwer: Erkläre anhand von drei selbst gewählten Beispielen den Unterschied zwischen == und ===. Warum kann == zu Bugs führen? Schreibe deine Beispiele als console.log()-Aufrufe mit Kommentaren.

Kapitel 4: Bedingungen

if, else if und else – Entscheidungen treffen. Mit Bedingungen kann ein Programm unterschiedliche Wege gehen, abhängig davon ob etwas wahr oder falsch ist. Das Grundprinzip: Wenn (if) eine Bedingung erfüllt ist, führe diesen Code aus. Sonst wenn (else if) eine andere Bedingung zutrifft, führe jenen Code aus. Und falls keine Bedingung zutrifft, führe den Standard-Code (else) aus. Du kannst beliebig viele else if-Zweige einbauen, aber es gibt immer nur ein if am Anfang und ein optionales else am Ende.

// Einfache if-else-Bedingung
const temperatur = 22;

if (temperatur > 30) {
  console.log("Es ist heiß!");
} else if (temperatur > 20) {
  console.log("Es ist angenehm warm.");
} else if (temperatur > 10) {
  console.log("Es ist kühl.");
} else {
  console.log("Es ist kalt!");
}
// Ausgabe: "Es ist angenehm warm."

// Mehrere Bedingungen mit logischen Operatoren
const stunde = 14;
if (stunde >= 6 && stunde < 12) {
  console.log("Guten Morgen!");
} else if (stunde >= 12 && stunde < 18) {
  console.log("Guten Tag!");
} else {
  console.log("Guten Abend!");
}

switch/case – wenn es viele Fälle gibt. Bei vielen festen Einzelfällen ist switch/case oft lesbarer als eine lange Kette von else if. Switch vergleicht den angegebenen Wert mit den case-Werten. Wichtig: Jeder case-Block sollte mit break enden, sonst "fällt" die Ausführung in den nächsten case (sogenanntes Fall-through). Der default-Block entspricht dem else und wird ausgeführt, wenn kein Fall passt.

// switch/case Beispiel
const wochentag = "Montag";

switch (wochentag) {
  case "Montag":
    console.log("Wochenstart!");
    break;
  case "Freitag":
    console.log("Fast Wochenende!");
    break;
  case "Samstag":
  case "Sonntag":
    // Mehrere cases können denselben Code ausführen
    console.log("Wochenende!");
    break;
  default:
    console.log("Ein normaler Arbeitstag.");
}

// switch mit Zahlen
const note = 1;
switch (note) {
  case 1: console.log("Sehr gut"); break;
  case 2: console.log("Gut"); break;
  case 3: console.log("Befriedigend"); break;
  default: console.log("Note: " + note);
}

Truthy und Falsy – was als wahr gilt. In JavaScript hat jeder Wert einen impliziten Wahrheitswert. Als "falsy" (unwahr) gelten: false, 0, "" (leerer String), null, undefined und NaN. Alles andere ist "truthy" (wahr) – also z.B. 1, "text", [] (leeres Array), {} (leeres Objekt). Dieses Konzept ist wichtig, um JavaScript-Code zu verstehen und zu schreiben.

Nullish Coalescing Operator (??). Der ??-Operator ist eine modernere Alternative zu || für Standardwerte. Er gibt den rechten Wert nur dann zurück, wenn der linke Wert null oder undefined ist – nicht bei anderen falsy-Werten wie 0 oder "". Das ist besonders nützlich, wenn 0 oder ein leerer String ein gültiger Wert sein soll.

// Short-Circuit Evaluation mit || und &&
const benutzername = "" || "Gast";   // "Gast" (weil "" falsy ist)
const benutzername2 = "Alice" || "Gast"; // "Alice" (wahr, also wird links genommen)

const istAngemeldet = true;
istAngemeldet && console.log("Willkommen!"); // Wird ausgeführt
false && console.log("Wird nie ausgeführt"); // Wird nicht ausgeführt

// Nullish Coalescing (??)
const punktzahl = 0;
const anzeige1 = punktzahl || "keine Punkte"; // "keine Punkte" (0 ist falsy!)
const anzeige2 = punktzahl ?? "keine Punkte"; // 0 (nur null/undefined löst aus)

console.log(anzeige1); // "keine Punkte" – oft nicht gewünscht!
console.log(anzeige2); // 0 – korrekt!
Übungsaufgaben
  1. Einfach: Schreibe eine if-else-Bedingung, die prüft ob eine Variable zahl positiv, negativ oder null ist und in jedem Fall eine passende Meldung ausgibt.
  2. Mittel: Erstelle ein switch/case für die Jahreszeiten: Frühling (März-Mai), Sommer (Juni-August), Herbst (September-November), Winter (Dezember-Februar). Verwende eine Variable monat mit einem Monatsnamen als String.
  3. Schwer: Erkläre den Unterschied zwischen || und ?? anhand eines Beispiels, bei dem beide unterschiedliche Ergebnisse liefern. Wann ist ?? die bessere Wahl? Schreibe deine Erklärung als Kommentare direkt im Code.

Kapitel 5: Funktionen

Was ist eine Funktion und warum brauchen wir sie? Eine Funktion ist ein benannter Block von Code, der eine bestimmte Aufgabe erfüllt und beliebig oft aufgerufen werden kann. Ohne Funktionen müsste man denselben Code immer wieder schreiben. Mit Funktionen schreibt man den Code einmal, gibt ihm einen Namen und ruft ihn bei Bedarf auf. Das macht Programme kürzer, übersichtlicher und leichter zu warten. Eine Funktion kann Eingabewerte (Parameter) entgegennehmen und ein Ergebnis zurückgeben (Rückgabewert).

Drei Wege, eine Funktion zu definieren. In JavaScript gibt es mehrere Syntaxvarianten. Die klassische Funktionsdeklaration mit dem Schlüsselwort function ist die älteste und verbreitetste Form. Sie wird "gehoisted" – das heißt, sie kann im Code aufgerufen werden, bevor sie definiert wird. Die Function Expression weist eine Funktion einer Variable zu und wird nicht gehoisted. Die Arrow Function (Pfeilfunktion, eingeführt mit ES6) ist eine kompakte Schreibweise, die besonders für kurze Funktionen und Callbacks beliebt ist.

// 1. Funktionsdeklaration (klassisch)
function begrüße(name) {
  return "Hallo, " + name + "!";
}
console.log(begrüße("Max")); // "Hallo, Max!"

// 2. Function Expression
const addiere = function(a, b) {
  return a + b;
};
console.log(addiere(3, 7)); // 10

// 3. Arrow Function (modern, empfohlen für einfache Fälle)
const multipliziere = (a, b) => a * b;
console.log(multipliziere(4, 5)); // 20

// Arrow Function mit Körper (für mehrere Zeilen)
const berechneRabatt = (preis, rabatt) => {
  const ersparnis = preis * (rabatt / 100);
  return preis - ersparnis;
};
console.log(berechneRabatt(100, 20)); // 80

Parameter und Standardwerte. Funktionen können Parameter mit Standardwerten definieren. Wird die Funktion ohne diesen Parameter aufgerufen, wird der Standardwert verwendet. Das ist eine elegante Lösung für optionale Parameter und ersetzt frühere Tricks mit ||.

// Standardwerte für Parameter
function begrüße(name = "Gast", sprache = "de") {
  if (sprache === "de") {
    return `Hallo, ${name}!`;
  } else if (sprache === "en") {
    return `Hello, ${name}!`;
  }
  return `Hi, ${name}!`;
}

console.log(begrüße("Alice", "de")); // "Hallo, Alice!"
console.log(begrüße("Bob", "en"));   // "Hello, Bob!"
console.log(begrüße());              // "Hallo, Gast!" (beide Standardwerte)
console.log(begrüße("Eve"));         // "Hallo, Eve!" (nur Name angegeben)

// Mehrere Rückgabewerte (als Array oder Objekt)
function minMax(zahlen) {
  return {
    min: Math.min(...zahlen),
    max: Math.max(...zahlen)
  };
}
const ergebnis = minMax([3, 1, 7, 2, 9]);
console.log(ergebnis.min); // 1
console.log(ergebnis.max); // 9

Scope – Gültigkeitsbereiche verstehen. Scope (Gültigkeitsbereich) bestimmt, wo im Code eine Variable sichtbar und nutzbar ist. Variablen, die mit let oder const innerhalb einer Funktion definiert werden, sind nur dort sichtbar (lokaler Scope). Variablen außerhalb aller Funktionen sind global und überall sichtbar. Das Problem mit var ist, dass es nicht Block-Scope sondern nur Funktions-Scope kennt – ein häufige Fehlerquelle, die let und const behoben haben.

Reine Funktionen und Seiteneffekte. Eine Funktion, die immer dasselbe Ergebnis liefert wenn sie mit denselben Eingaben aufgerufen wird, und die keine externen Variablen verändert, nennt man eine "reine Funktion". Das ist ein wichtiges Konzept in der funktionalen Programmierung. Reine Funktionen sind leichter zu testen, zu debuggen und zu verstehen. Vermeide es, globale Variablen innerhalb von Funktionen zu verändern.

Übungsaufgaben
  1. Einfach: Schreibe eine Funktion quadrat(zahl), die das Quadrat einer Zahl berechnet und zurückgibt. Teste sie mit verschiedenen Eingaben.
  2. Mittel: Schreibe eine Funktion istPrim(zahl), die zurückgibt ob eine Zahl eine Primzahl ist (nur durch 1 und sich selbst teilbar). Teste sie mit 2, 3, 4, 7, 9, 11.
  3. Schwer: Schreibe dieselbe Funktion dreimal: als Funktionsdeklaration, als Function Expression und als Arrow Function. Was sind die Unterschiede im Verhalten? Teste, ob eine Funktionsdeklaration wirklich gehoisted wird, indem du sie vor ihrer Definition aufrufst.

Kapitel 6: Arrays

Was ist ein Array? Ein Array ist eine geordnete Liste von Werten. Während eine Variable nur einen einzigen Wert speichern kann, kann ein Array beliebig viele Werte enthalten – und auf jeden einzelnen per Nummer (Index) zugreifen. Indizes beginnen immer bei 0, nicht bei 1. Das ist einer der häufigsten Stolpersteine für Einsteiger: Das erste Element hat den Index 0, das zweite den Index 1, und so weiter.

// Array erstellen
const früchte = ["Apfel", "Banane", "Kirsche", "Dattel"];

// Zugriff per Index (beginnt bei 0!)
console.log(früchte[0]); // "Apfel"
console.log(früchte[2]); // "Kirsche"
console.log(früchte[früchte.length - 1]); // "Dattel" (letztes Element)

// Array-Eigenschaften
console.log(früchte.length); // 4

// Elemente hinzufügen und entfernen
früchte.push("Erdbeere");    // ans Ende hinzufügen
console.log(früchte);        // ["Apfel", "Banane", "Kirsche", "Dattel", "Erdbeere"]

früchte.pop();               // letztes Element entfernen
console.log(früchte);        // ["Apfel", "Banane", "Kirsche", "Dattel"]

früchte.unshift("Ananas");   // an den Anfang hinzufügen
früchte.shift();             // erstes Element entfernen

// indexOf und includes
console.log(früchte.indexOf("Banane")); // 1
console.log(früchte.includes("Kirsche")); // true

Die wichtigsten Array-Methoden: forEach, map, filter. Diese drei Methoden sind das Herzstück der modernen JavaScript-Entwicklung. forEach führt eine Funktion für jedes Element aus (ohne Rückgabewert). map erstellt ein neues Array, bei dem jedes Element transformiert wurde. filter erstellt ein neues Array, das nur die Elemente enthält, für die eine Bedingung zutrifft. Diese Methoden sind eleganter und sicherer als traditionelle for-Schleifen.

const zahlen = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// forEach: Jedes Element ausgeben
zahlen.forEach(zahl => console.log(zahl));

// map: Jedes Element verdoppeln (neues Array)
const verdoppelt = zahlen.map(zahl => zahl * 2);
console.log(verdoppelt); // [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

// filter: Nur gerade Zahlen behalten (neues Array)
const geradeZahlen = zahlen.filter(zahl => zahl % 2 === 0);
console.log(geradeZahlen); // [2, 4, 6, 8, 10]

// find: Erstes Element, das Bedingung erfüllt
const ersteGerade = zahlen.find(zahl => zahl % 2 === 0);
console.log(ersteGerade); // 2

// Kombination: filter + map
const großeGeradeQuadrate = zahlen
  .filter(z => z % 2 === 0)   // nur gerade Zahlen
  .map(z => z * z)            // quadrieren
  .filter(z => z > 20);       // nur Werte > 20
console.log(großeGeradeQuadrate); // [36, 64, 100]

Splice und Slice – Teile eines Arrays extrahieren oder ersetzen. slice(start, end) gibt einen Teil des Arrays zurück, ohne das Original zu verändern. splice(start, anzahl, ...neue)` verändert das Original direkt – es kann Elemente entfernen und neue einfügen. Diese beiden verwechseln Einsteiger oft, weil die Namen ähnlich klingen.

const buchstaben = ["a", "b", "c", "d", "e"];

// slice: Teil ausschneiden (Original bleibt unverändert)
const teil = buchstaben.slice(1, 4); // von Index 1 bis 3 (4 ist exklusiv)
console.log(teil);        // ["b", "c", "d"]
console.log(buchstaben);  // ["a", "b", "c", "d", "e"] – unverändert!

// splice: Original verändern
buchstaben.splice(2, 1, "X", "Y"); // ab Index 2: 1 Element entfernen, "X" und "Y" einsetzen
console.log(buchstaben); // ["a", "b", "X", "Y", "d", "e"]

// sort: Arrays sortieren
const namen = ["Zara", "Alice", "Mike", "Bob"];
namen.sort();
console.log(namen); // ["Alice", "Bob", "Mike", "Zara"]

// Zahlen sort benötigt eine Vergleichsfunktion!
const nums = [10, 2, 21, 1];
nums.sort((a, b) => a - b); // aufsteigend
console.log(nums); // [1, 2, 10, 21]
Übungsaufgaben
  1. Einfach: Erstelle ein Array mit fünf deiner Lieblingsfilme. Gib den ersten und letzten Film aus. Füge einen neuen Film am Ende hinzu und entferne den ersten Film.
  2. Mittel: Gegeben: const preise = [12.5, 3.99, 27.0, 8.49, 15.0]. Nutze filter() um alle Preise über 10 € herauszufiltern, und map() um auf alle Preise 19% MwSt aufzuschlagen (Preis * 1.19). Kombiniere beide in einer Kette.
  3. Schwer: Schreibe eine Funktion flatten(arr), die ein verschachteltes Array (z.B. [1, [2, 3], [4, [5, 6]]]) in ein flaches Array umwandelt. Recherchiere die Array-Methode flat() und erkläre, wie sie funktioniert.

Kapitel 7: Objekte

Was ist ein Objekt? Ein Objekt ist eine Sammlung von zusammengehörigen Daten und Funktionalitäten. Während ein Array Elemente per Index (0, 1, 2, ...) speichert, speichert ein Objekt Elemente per Namen (sogenannte Schlüssel oder Properties). Jede Eigenschaft besteht aus einem Schlüssel-Wert-Paar: schlüssel: wert. Objekte sind das zentrale Konzept in JavaScript – fast alles in JavaScript ist ein Objekt oder verhält sich wie eines.

// Objekt erstellen mit Objekt-Literal-Notation
const person = {
  vorname: "Alice",
  nachname: "Müller",
  alter: 28,
  istEntwicklerin: true,
  adresse: {
    stadt: "Berlin",
    plz: "10115"
  }
};

// Eigenschaften lesen
console.log(person.vorname);          // "Alice" (Punkt-Notation)
console.log(person["nachname"]);      // "Müller" (Bracket-Notation)
console.log(person.adresse.stadt);   // "Berlin" (verschachtelter Zugriff)

// Eigenschaften schreiben (verändern)
person.alter = 29;
person.beruf = "Softwareentwicklerin"; // Neue Eigenschaft hinzufügen

// Eigenschaft löschen
delete person.beruf;
console.log(person.beruf); // undefined

// Eigenschaft prüfen
console.log("vorname" in person); // true
console.log("beruf" in person);   // false

Methoden in Objekten. Objekte können nicht nur Daten, sondern auch Funktionen als Eigenschaften haben. Diese Funktionen nennt man Methoden. Innerhalb einer Methode kann man mit dem Schlüsselwort this auf andere Eigenschaften des eigenen Objekts zugreifen. Achtung: Bei Arrow Functions verhält sich this anders – daher werden Methoden in Objekten traditionell mit der regulären function-Syntax geschrieben.

const auto = {
  marke: "Toyota",
  modell: "Corolla",
  baujahr: 2020,
  istAngemeldet: true,

  // Methode im Objekt
  vorstellen: function() {
    return `${this.marke} ${this.modell} (${this.baujahr})`;
  },

  alter: function() {
    return new Date().getFullYear() - this.baujahr;
  }
};

console.log(auto.vorstellen()); // "Toyota Corolla (2020)"
console.log(`Das Auto ist ${auto.alter()} Jahre alt.`);

// Kurzschreibweise für Methoden (ES6+)
const taschenrechner = {
  addiere(a, b) { return a + b; },
  subtrahiere(a, b) { return a - b; }
};
console.log(taschenrechner.addiere(10, 5)); // 15

for...in – über Objekteigenschaften iterieren. Mit der for...in-Schleife kannst du über alle Schlüssel eines Objekts iterieren. Für Arrays sollte stattdessen for...of oder forEach verwendet werden. Mit Object.keys(), Object.values() und Object.entries() erhältst du alle Schlüssel, alle Werte oder beides als Array.

const produkt = {
  name: "Laptop",
  preis: 999,
  lagerbestand: 42,
  kategorie: "Elektronik"
};

// for...in Schleife
for (const schlüssel in produkt) {
  console.log(`${schlüssel}: ${produkt[schlüssel]}`);
}

// Object.keys(), Object.values(), Object.entries()
console.log(Object.keys(produkt));
// ["name", "preis", "lagerbestand", "kategorie"]

console.log(Object.values(produkt));
// ["Laptop", 999, 42, "Elektronik"]

Object.entries(produkt).forEach(([schlüssel, wert]) => {
  console.log(`${schlüssel} = ${wert}`);
});

// Objekte vs. Arrays
// Verwende ein Array wenn: die Reihenfolge wichtig ist, du per Index zugreifst
// Verwende ein Objekt wenn: du Daten mit Namen versehen willst, die Reihenfolge egal ist
Übungsaufgaben
  1. Einfach: Erstelle ein Objekt für ein Buch mit den Eigenschaften Titel, Autor, Seitenzahl und Erscheinungsjahr. Lies alle vier Eigenschaften aus und gib sie mit Template Literals in einem Satz aus.
  2. Mittel: Füge dem Buch-Objekt eine Methode kurzbeschreibung() hinzu, die einen formatierten Text zurückgibt. Verwende this um auf die Eigenschaften des Objekts zuzugreifen.
  3. Schwer: Erstelle ein Array von Objekt-Personen (mindestens 4), jede mit Name und Alter. Schreibe eine Funktion, die dieses Array nach Alter sortiert und nur die Personen zurückgibt, die älter als 18 sind. Kombiniere Methoden aus Kapitel 6 und 7.

Kapitel 8: Schleifen

Warum braucht man Schleifen? Schleifen ermöglichen es, denselben Code mehrfach auszuführen, ohne ihn wiederholt zu schreiben. Stell dir vor, du möchtest die Zahlen 1 bis 100 ausgeben – mit einer Schleife sind das zwei Zeilen Code, ohne Schleife wären es 100 Zeilen. JavaScript kennt verschiedene Arten von Schleifen für verschiedene Anwendungsfälle: for, while, do...while, for...of und for...in.

// for-Schleife: wenn die Anzahl der Durchläufe bekannt ist
for (let i = 0; i < 5; i++) {
  console.log(`Durchlauf ${i}`); // 0, 1, 2, 3, 4
}

// Countdown
for (let i = 10; i >= 0; i--) {
  console.log(i);
}
console.log("Start!");

// Verschachtelte for-Schleife
for (let i = 1; i <= 3; i++) {
  for (let j = 1; j <= 3; j++) {
    console.log(`${i} x ${j} = ${i * j}`);
  }
}

while und do...while. Die while-Schleife prüft ihre Bedingung am Anfang – ist sie von Beginn an falsch, wird der Körper nie ausgeführt. Die do...while-Schleife führt den Körper immer mindestens einmal aus und prüft die Bedingung danach. Das ist nützlich, wenn du mindestens eine Ausführung garantieren möchtest, z.B. bei Benutzereingaben.

// while-Schleife
let zahl = 1;
while (zahl <= 5) {
  console.log(zahl);
  zahl++; // WICHTIG: ohne Inkrement → Endlosschleife!
}

// do...while (mindestens 1 Ausführung)
let eingabe;
let versuch = 0;
do {
  versuch++;
  eingabe = versuch * 3; // Simulation einer Eingabe
  console.log(`Versuch ${versuch}: Wert = ${eingabe}`);
} while (eingabe < 10);
// Schleife läuft, bis eingabe >= 10

// for...of: für Arrays und andere iterierbare Werte (empfohlen)
const städte = ["Berlin", "München", "Hamburg", "Köln"];
for (const stadt of städte) {
  console.log(stadt);
}

// for...in: für Objekte (über Schlüssel iterieren)
const konfiguration = { sprache: "de", dunkelModus: true, schriftgröße: 16 };
for (const schlüssel in konfiguration) {
  console.log(`${schlüssel}: ${konfiguration[schlüssel]}`);
}

break und continue – Schleifen steuern. Mit break verlässt du eine Schleife sofort, egal wie viele Durchläufe noch geplant wären. Mit continue springst du zum nächsten Durchlauf und überspringst den Rest des aktuellen Schleifenkörpers. Beide können verwendet werden, um Sonderfälle zu behandeln oder früh aus einer Suche herauszuspringen.

// break: Schleife verlassen
const zahlen = [3, 7, 12, 5, 9, 1];
let gesuchte = -1;

for (let i = 0; i < zahlen.length; i++) {
  if (zahlen[i] > 10) {
    gesuchte = zahlen[i];
    break; // Suche beenden, sobald wir gefunden haben
  }
}
console.log(`Erste Zahl > 10: ${gesuchte}`); // 12

// continue: Durchlauf überspringen
for (let i = 1; i <= 10; i++) {
  if (i % 2 === 0) continue; // gerade Zahlen überspringen
  console.log(i); // nur ungerade: 1, 3, 5, 7, 9
}

// Typischer Anwendungsfall: Validierung überspringen
const daten = ["Alice", "", "Bob", null, "Charlie"];
for (const name of daten) {
  if (!name) continue; // leere/null Werte überspringen
  console.log(`Verarbeite: ${name}`);
}
Übungsaufgaben
  1. Einfach: Schreibe eine for-Schleife, die das 1×1 des Einmaleins für die Zahl 7 ausgibt (7×1=7, 7×2=14, ... 7×10=70).
  2. Mittel: Schreibe eine Schleife, die alle Primzahlen zwischen 2 und 50 findet und ausgibt. Verwende eine innere Schleife (oder deine Funktion aus Kapitel 5) zur Prüfung.
  3. Schwer: Erstelle ein Array von 10 zufälligen Zahlen (nutze Math.random() und Math.floor()). Berechne Summe, Durchschnitt, Minimum und Maximum mit einer einzigen Schleife. Nutze keine Array-Methoden wie Math.min() direkt auf das Array.

Kapitel 9: DOM-Manipulation

Voraussetzung: Dieses Kapitel baut auf allem auf, was du in Kapitel 1–8 gelernt hast – besonders Variablen (Kapitel 2), Funktionen (Kapitel 5) und Arrow Functions. Falls du ein Kapitel übersprungen hast, kehre zuerst zurück. DOM-Manipulation ist mächtig, aber ohne diese Grundlagen schwer zu verstehen.

Was ist das DOM? Das Document Object Model (DOM) ist die Programmierschnittstelle des Browsers zu einer HTML-Seite. Wenn der Browser eine HTML-Datei lädt, baut er daraus einen Baum aus Objekten auf – jedes HTML-Element wird zu einem Knoten in diesem Baum. JavaScript kann diesen Baum lesen, verändern, neue Knoten hinzufügen und vorhandene entfernen. Das ist der Kern von Webinteraktivität: JavaScript greift über das DOM auf die Seite zu.

Elemente finden. Bevor du ein Element verändern kannst, musst du es finden. Die wichtigsten Methoden dafür: getElementById sucht ein Element mit einer bestimmten ID (IDs sollten einmalig sein). querySelector gibt das erste Element zurück, das einem CSS-Selektor entspricht. querySelectorAll gibt alle passenden Elemente als NodeList zurück.

// HTML-Elemente finden
const titel = document.getElementById("haupt-titel");
const ersterButton = document.querySelector("button");
const alleButtons = document.querySelectorAll("button");
const eingabefeld = document.querySelector("#email-input");
const ersteKarte = document.querySelector(".karte");
const alleKarten = document.querySelectorAll(".karte");

// Durch mehrere Elemente iterieren
alleKarten.forEach(karte => {
  console.log(karte.textContent);
});

// Elemente im Kontext eines anderen Elements suchen
const navigation = document.querySelector("nav");
const navLinks = navigation.querySelectorAll("a"); // nur Links in der Nav

Inhalte lesen und verändern. Sobald du ein Element gefunden hast, kannst du seinen Inhalt und sein Aussehen ändern. textContent liest und setzt den reinen Textinhalt ohne HTML-Formatierung. innerHTML erlaubt auch HTML-Tags (Vorsicht: niemals ungeprüfte Nutzereingaben hier einfügen – Sicherheitslücke!). Mit style kannst du CSS-Eigenschaften direkt setzen. Mit classList steuerst du CSS-Klassen.

// Inhalt lesen und setzen
const überschrift = document.querySelector("h1");
console.log(überschrift.textContent); // aktuellen Text lesen
überschrift.textContent = "Neuer Titel!"; // Text setzen

const container = document.querySelector(".container");
container.innerHTML = "<strong>Fett</strong> und <em>kursiv</em>";

// Style-Eigenschaften setzen (camelCase statt Bindestriche!)
const box = document.querySelector(".box");
box.style.backgroundColor = "blue"; // CSS: background-color
box.style.fontSize = "24px";         // CSS: font-size
box.style.display = "none";          // Element verstecken

// classList – CSS-Klassen steuern (empfohlen!)
box.classList.add("aktiv");         // Klasse hinzufügen
box.classList.remove("inaktiv");    // Klasse entfernen
box.classList.toggle("hervorgehoben"); // Klasse umschalten
box.classList.contains("aktiv");    // prüfen ob Klasse vorhanden → true/false

Neue Elemente erstellen und einfügen. Mit createElement erstellst du ein neues HTML-Element im Speicher. Dann fügst du es mit appendChild, prepend oder insertBefore in die Seite ein. Mit remove() entfernst du ein Element wieder. Diese Techniken sind die Grundlage für dynamische Benutzeroberflächen – zum Beispiel wenn eine Todo-Liste um neue Einträge erweitert wird.

// Neues Element erstellen
const neuerAbsatz = document.createElement("p");
neuerAbsatz.textContent = "Ich wurde dynamisch erstellt!";
neuerAbsatz.classList.add("dynamisch");

// Element einfügen
const hauptbereich = document.querySelector("main");
hauptbereich.appendChild(neuerAbsatz); // am Ende einfügen
hauptbereich.prepend(neuerAbsatz);     // am Anfang einfügen

// Attribut setzen
const neuerLink = document.createElement("a");
neuerLink.href = "https://www.example.com";
neuerLink.textContent = "Klick mich!";
neuerLink.setAttribute("target", "_blank");

// Element entfernen
const altesElement = document.querySelector(".alt");
if (altesElement) {
  altesElement.remove();
}
Übungsaufgaben
  1. Einfach: Erstelle eine HTML-Seite mit einem <h1> und einem Button. Wenn der Button geklickt wird (du lernst Events im nächsten Kapitel), soll der Text der Überschrift sich ändern. Füge zuerst alles manuell im HTML ein und ändere dann den Text per JavaScript in der Konsole.
  2. Mittel: Wähle mit querySelectorAll alle Absätze einer Seite aus und ändere ihre Hintergrundfarbe auf ein helles Gelb (#fef9c3). Verwende forEach und style.backgroundColor.
  3. Schwer: Erstelle eine Funktion erstelleKarte(titel, text), die ein neues <article>-Element mit einer <h3>-Überschrift und einem <p>-Absatz erstellt und es in einen bestehenden Container einfügt. Rufe die Funktion dreimal mit verschiedenen Inhalten auf.

Kapitel 10: Events

Was ist ein Event? Ein Event (Ereignis) ist etwas, das im Browser passiert – ein Klick auf einen Button, eine Tastatureingabe, das Laden der Seite, das Bewegen der Maus. JavaScript kann auf diese Events "hören" und darauf reagieren. Das Herzstück dafür ist die Methode addEventListener. Sie verbindet ein HTML-Element, einen Event-Typ (als String) und eine Funktion, die beim Eintreten des Events aufgerufen wird.

// Grundstruktur von addEventListener
element.addEventListener("eventtyp", function(event) {
  // Code, der ausgeführt wird
});

// Mit Arrow Function (modern)
const button = document.querySelector("#mein-button");
button.addEventListener("click", (event) => {
  console.log("Geklickt!");
  console.log(event);        // Das Event-Objekt mit allen Infos
  console.log(event.target); // Das Element, das geklickt wurde
});

// Mehrere Events auf einem Element
button.addEventListener("mouseover", () => button.style.background = "blue");
button.addEventListener("mouseout", () => button.style.background = "");

Häufige Event-Typen. Es gibt Dutzende von Event-Typen in JavaScript. Die wichtigsten für den Einstieg: click (Mausklick), input (Texteingabe in Echtzeit), change (Wert geändert und Feld verlassen), submit (Formular abgesendet), keydown / keyup (Taste gedrückt/losgelassen), mouseover / mouseout (Maus über Element), DOMContentLoaded (HTML vollständig geladen).

// input-Event: Eingabe in Echtzeit verarbeiten
const eingabe = document.querySelector("#text-input");
const vorschau = document.querySelector("#vorschau");

eingabe.addEventListener("input", (event) => {
  vorschau.textContent = event.target.value;
});

// keydown-Event: auf bestimmte Tasten reagieren
document.addEventListener("keydown", (event) => {
  console.log(`Taste gedrückt: ${event.key}`);
  if (event.key === "Escape") {
    console.log("ESC gedrückt – Modal schließen!");
  }
  if (event.key === "Enter") {
    console.log("Enter gedrückt!");
  }
});

// DOMContentLoaded: sicherstellen, dass alles geladen ist
document.addEventListener("DOMContentLoaded", () => {
  console.log("Seite vollständig geladen!");
  // Hier sicher auf DOM-Elemente zugreifen
});

Formulare und event.preventDefault(). Beim Absenden eines Formulars lädt der Browser standardmäßig die Seite neu. Mit event.preventDefault() kann dieses Standardverhalten unterdrückt werden – so kann JavaScript das Formular verarbeiten, ohne die Seite neu zu laden. Das ist essenziell für moderne Web-Apps.

// Formular-Submit abfangen
const formular = document.querySelector("#anmelde-formular");

formular.addEventListener("submit", (event) => {
  event.preventDefault(); // Seitenneuladen verhindern!

  const emailFeld = document.querySelector("#email");
  const passwortFeld = document.querySelector("#passwort");

  const email = emailFeld.value.trim();
  const passwort = passwortFeld.value;

  // Einfache Validierung
  if (!email.includes("@")) {
    console.log("Ungültige E-Mail-Adresse!");
    return;
  }
  if (passwort.length < 8) {
    console.log("Passwort zu kurz (min. 8 Zeichen)!");
    return;
  }

  console.log(`Anmeldung für: ${email}`);
  // Hier würde man normalerweise die Daten an einen Server senden
});

Event Delegation – effizient Events verwalten. Anstatt jeden einzelnen Button oder jeden einzelnen Listeneintrag mit einem Event-Listener zu versehen, kann man den Listener auf ein gemeinsames Elternelement setzen und mit event.target prüfen, welches Kind-Element geklickt wurde. Das ist effizienter und funktioniert sogar für dynamisch hinzugefügte Elemente.

// Event Delegation
const liste = document.querySelector("#aufgaben-liste");

liste.addEventListener("click", (event) => {
  // Prüfen, welches Kind-Element geklickt wurde
  if (event.target.classList.contains("loeschen-btn")) {
    const aufgabe = event.target.closest("li");
    aufgabe.remove();
  }

  if (event.target.tagName === "LI") {
    event.target.classList.toggle("erledigt");
  }
});
Übungsaufgaben
  1. Einfach: Erstelle eine Seite mit einem Button und einem Zähler (Startwert 0). Jeder Klick auf den Button soll den Zähler um 1 erhöhen und den aktuellen Wert anzeigen.
  2. Mittel: Baue ein Eingabefeld und ein Ausgabe-<p>. Bei jeder Eingabe (input-Event) soll der getippte Text rückwärts im Ausgabe-Absatz angezeigt werden. Hinweis: text.split("").reverse().join("").
  3. Schwer: Erstelle ein Formular mit Felder für Name, E-Mail und Alter. Beim Absenden (submit) soll event.preventDefault() aufgerufen werden. Validiere alle Felder: Name darf nicht leer sein, E-Mail muss @ enthalten, Alter muss zwischen 0 und 120 liegen. Zeige Fehler direkt neben den Feldern an.

Kapitel 11: Fehlerbehandlung & Debugging

Fehler gehören dazu – und du kannst sie beherrschen. Jeder Programmierer macht Fehler. Der Unterschied zwischen Anfängern und Profis liegt nicht darin, keine Fehler zu machen, sondern darin, Fehler schnell zu finden und zu beheben. JavaScript unterscheidet zwischen drei Arten von Fehlern: Syntaxfehler (falscher Code-Aufbau, wird vor der Ausführung erkannt), Laufzeitfehler (tritt während der Ausführung auf, z.B. wenn eine Variable nicht existiert) und Logikfehler (der Code läuft ohne Absturz, liefert aber das falsche Ergebnis – der schwierigste Fehlertyp).

// Häufige JavaScript-Fehlertypen
// 1. SyntaxError: Code kann nicht geparst werden
// let x = ;  // SyntaxError: Unexpected token

// 2. ReferenceError: Variable existiert nicht
try {
  console.log(nichtDefinierteVariable);
} catch (fehler) {
  console.log(fehler.name);    // "ReferenceError"
  console.log(fehler.message); // "nichtDefinierteVariable is not defined"
}

// 3. TypeError: falscher Typ für Operation
try {
  const zahl = 42;
  zahl.toUpperCase(); // Zahlen haben keine toUpperCase-Methode
} catch (fehler) {
  console.log(fehler.name);    // "TypeError"
  console.log(fehler.message); // zahl.toUpperCase is not a function
}

try/catch/finally – Fehler abfangen. Mit try/catch kannst du Code ausführen, der möglicherweise einen Fehler wirft, und den Fehler kontrolliert behandeln, anstatt das Programm abstürzen zu lassen. Der finally-Block wird immer ausgeführt, egal ob ein Fehler aufgetreten ist oder nicht – er eignet sich für Aufräumarbeiten.

// try/catch/finally
function dividiere(a, b) {
  try {
    if (b === 0) {
      throw new Error("Division durch Null ist nicht erlaubt!");
    }
    return a / b;
  } catch (fehler) {
    console.error("Fehler:", fehler.message);
    return null;
  } finally {
    console.log("Berechnung abgeschlossen (immer).");
  }
}

console.log(dividiere(10, 2));  // 5
console.log(dividiere(10, 0));  // Fehler + null

// Eigene Fehler werfen
function validiereAlter(alter) {
  if (typeof alter !== "number") {
    throw new TypeError("Alter muss eine Zahl sein!");
  }
  if (alter < 0 || alter > 150) {
    throw new RangeError("Alter muss zwischen 0 und 150 liegen!");
  }
  return true;
}

try {
  validiereAlter("zwanzig");
} catch (fehler) {
  console.log(`${fehler.name}: ${fehler.message}`);
}

Die Konsole als Debugging-Werkzeug. Die Browser-Konsole bietet mehr als nur console.log(). console.warn() gibt eine gelbe Warnung aus, console.error() eine rote Fehlermeldung. console.table() zeigt Arrays und Objekte als schöne Tabelle. console.time() und console.timeEnd() messen die Ausführungszeit. Und console.group() gruppiert Ausgaben übersichtlich.

// Konsolen-Methoden
console.log("Normal:", "eine Nachricht");
console.warn("Warnung:", "etwas stimmt nicht ganz");
console.error("Fehler:", "etwas ist schiefgelaufen");

// Objekte und Arrays schön anzeigen
const personen = [
  { name: "Alice", alter: 28 },
  { name: "Bob",   alter: 34 },
  { name: "Eve",   alter: 25 }
];
console.table(personen);

// Zeit messen
console.time("meinTest");
for (let i = 0; i < 1000000; i++) { /* nichts */ }
console.timeEnd("meinTest"); // gibt Millisekunden aus

// Typeof-Prüfungen zur Absicherung
function sicheAddition(a, b) {
  if (typeof a !== "number" || typeof b !== "number") {
    throw new TypeError("Beide Parameter müssen Zahlen sein!");
  }
  return a + b;
}

Breakpoints und der Debugger. Für komplexere Fehler reicht console.log() nicht aus. Im Browser (F12 → Sources/Quellen) kannst du Breakpoints setzen: Der Code hält an dieser Stelle an und du kannst Variablenwerte, den Aufruf-Stack und den weiteren Programmfluss Schritt für Schritt verfolgen. Du kannst auch das Schlüsselwort debugger; in deinen Code schreiben – der Browser hält automatisch an dieser Stelle an, wenn die Entwicklertools geöffnet sind.

Übungsaufgaben
  1. Einfach: Schreibe absichtlich drei verschiedene Arten von Fehlern in der Konsole (SyntaxError, ReferenceError, TypeError). Schau dir die Fehlermeldungen genau an. Was verrät dir die Fehlermeldung über den Ort und die Art des Fehlers?
  2. Mittel: Schreibe eine Funktion parseZahl(eingabe), die einen String in eine Zahl umwandelt (Number(eingabe) oder parseInt(eingabe)). Verwende try/catch, um den Fall zu behandeln, dass die Eingabe keine gültige Zahl ist (NaN). Gib in diesem Fall einen hilfreichen Fehlertext zurück.
  3. Schwer: Schreibe eine Funktion holeDaten(url), die eine beliebige öffentliche API aufruft (z.B. https://jsonplaceholder.typicode.com/todos/1). Verwende fetch() und behandle sowohl Netzwerkfehler als auch den Fall, dass der Server einen Fehler zurückgibt (HTTP-Statuscode >= 400). Gib sinnvolle Fehlermeldungen aus.

Kapitel 12: Mini-Projekt – Interaktiver Taschenrechner

Was wir bauen. In diesem Kapitel bauen wir einen voll funktionsfähigen Taschenrechner im Browser. Das Projekt verbindet DOM-Manipulation (Kapitel 9) und Events (Kapitel 10) mit Bedingungen und Operatoren aus den früheren Kapiteln. Der Taschenrechner hat ein Anzeigefeld und Buttons für Zahlen (0–9), Operatoren (+, -, *, /), ein Gleichheitszeichen, eine Komma-Taste und eine Lösch-Taste.

Der HTML-Grundaufbau. Zuerst legen wir die Struktur im HTML fest. Wir brauchen ein Display-Element für die Anzeige und eine Reihe von Buttons. Jeder Button bekommt ein data-value-Attribut – das ist eine HTML5-Technik, um beliebige Daten an ein Element zu hängen, die JavaScript dann auslesen kann.

<!-- HTML-Struktur des Taschenrechners -->
<div class="rechner">
  <div id="display" class="rechner-display">0</div>
  <div class="rechner-buttons">
    <button data-value="C" class="btn-sonder">C</button>
    <button data-value="+/-" class="btn-sonder">+/-</button>
    <button data-value="%" class="btn-sonder">%</button>
    <button data-value="/" class="btn-op">÷</button>

    <button data-value="7">7</button>
    <button data-value="8">8</button>
    <button data-value="9">9</button>
    <button data-value="*" class="btn-op">×</button>

    <button data-value="4">4</button>
    <button data-value="5">5</button>
    <button data-value="6">6</button>
    <button data-value="-" class="btn-op">−</button>

    <button data-value="1">1</button>
    <button data-value="2">2</button>
    <button data-value="3">3</button>
    <button data-value="+" class="btn-op">+</button>

    <button data-value="0" class="btn-null">0</button>
    <button data-value=".">,</button>
    <button data-value="=" class="btn-gleich">=</button>
  </div>
</div>

Die JavaScript-Logik. Der Taschenrechner nutzt drei Variablen: den aktuellen Eingabewert (aktuellerWert), den gespeicherten ersten Operanden (ersterOperand) und den ausgewählten Operator (operator). Bei jedem Button-Klick wird geprüft, was gedrückt wurde, und entsprechend gehandelt. Event Delegation macht den Code besonders sauber: ein einziger Event-Listener auf dem Button-Container reicht aus.

// JavaScript-Logik des Taschenrechners
const display = document.getElementById("display");
const buttons = document.querySelector(".rechner-buttons");

let aktuellerWert = "0";
let ersterOperand = null;
let operator = null;
let warteAufZweitenOperanden = false;

function zeigeAnzeige(wert) {
  display.textContent = wert;
}

function verarbeiteEingabe(wert) {
  // Zahl gedrückt
  if (!isNaN(wert) || wert === ".") {
    if (warteAufZweitenOperanden) {
      aktuellerWert = wert === "." ? "0." : wert;
      warteAufZweitenOperanden = false;
    } else {
      if (wert === "." && aktuellerWert.includes(".")) return;
      aktuellerWert = aktuellerWert === "0" && wert !== "."
        ? wert
        : aktuellerWert + wert;
    }
    zeigeAnzeige(aktuellerWert);
    return;
  }

  // Löschen
  if (wert === "C") {
    aktuellerWert = "0";
    ersterOperand = null;
    operator = null;
    warteAufZweitenOperanden = false;
    zeigeAnzeige("0");
    return;
  }

  // Vorzeichen wechseln
  if (wert === "+/-") {
    aktuellerWert = String(parseFloat(aktuellerWert) * -1);
    zeigeAnzeige(aktuellerWert);
    return;
  }

  // Prozent
  if (wert === "%") {
    aktuellerWert = String(parseFloat(aktuellerWert) / 100);
    zeigeAnzeige(aktuellerWert);
    return;
  }

  // Operator (+, -, *, /)
  if (["+", "-", "*", "/"].includes(wert)) {
    ersterOperand = parseFloat(aktuellerWert);
    operator = wert;
    warteAufZweitenOperanden = true;
    return;
  }

  // Gleichheitszeichen
  if (wert === "=") {
    if (operator === null || ersterOperand === null) return;
    const zweiterOperand = parseFloat(aktuellerWert);
    let ergebnis;
    switch (operator) {
      case "+": ergebnis = ersterOperand + zweiterOperand; break;
      case "-": ergebnis = ersterOperand - zweiterOperand; break;
      case "*": ergebnis = ersterOperand * zweiterOperand; break;
      case "/":
        ergebnis = zweiterOperand === 0
          ? "Fehler"
          : ersterOperand / zweiterOperand;
        break;
    }
    aktuellerWert = String(ergebnis);
    ersterOperand = null;
    operator = null;
    warteAufZweitenOperanden = false;
    zeigeAnzeige(aktuellerWert);
  }
}

// Event Delegation auf den Button-Container
buttons.addEventListener("click", (event) => {
  if (event.target.tagName !== "BUTTON") return;
  verarbeiteEingabe(event.target.dataset.value);
});
Übungsaufgaben
  1. Einfach: Baue den Taschenrechner nach dem obigen Code-Beispiel nach. Stelle sicher, dass Addition, Subtraktion, Multiplikation und Division korrekt funktionieren. Teste den Sonderfall Division durch Null.
  2. Mittel: Erweitere den Taschenrechner um eine Tastenhistorie: Alle bisherigen Eingaben sollen als kleiner Text über dem Display angezeigt werden (z.B. "8 + 5 ="). Führe eine neue Variable verlauf ein und aktualisiere sie bei jedem Schritt.
  3. Schwer: Füge dem Taschenrechner Tastatur-Support hinzu: Wenn eine Taste auf der Tastatur gedrückt wird (0-9, +, -, *, /, Enter, Escape), soll der Taschenrechner genauso reagieren wie bei einem Button-Klick. Verwende das keydown-Event auf document.

Kapitel 13: Mini-Projekt – Todo-Liste

Was wir bauen. Eine Todo-Liste ist das klassische Einsteiger-Projekt für JavaScript-Entwickler – und das aus gutem Grund. Es vereint fast alle gelernten Konzepte: DOM-Manipulation, Events, Arrays, Objekte und Bedingungen. Unsere Implementierung erlaubt es, neue Aufgaben hinzuzufügen, sie als erledigt abzuhaken und sie wieder zu löschen. Zudem speichern wir die Aufgaben im localStorage, damit sie auch nach einem Seitenneuladen erhalten bleiben.

HTML-Struktur der Todo-Liste. Wir brauchen ein Eingabefeld mit einem "Hinzufügen"-Button und eine Liste für die Einträge. Jeder Eintrag besteht aus einer Checkbox, dem Aufgabentext und einem Löschen-Button. Wir bauen die Einträge dynamisch mit JavaScript auf.

<!-- HTML-Struktur der Todo-Liste -->
<div class="todo-app">
  <h2>Meine Aufgaben</h2>

  <div class="todo-eingabe">
    <input
      type="text"
      id="aufgaben-input"
      placeholder="Neue Aufgabe eingeben..."
      autocomplete="off"
    />
    <button id="hinzufuegen-btn">Hinzufügen</button>
  </div>

  <ul id="aufgaben-liste"></ul>

  <div class="todo-footer">
    <span id="offen-zaehler">0 offen</span>
    <button id="erledigt-loeschen">Erledigte löschen</button>
  </div>
</div>

Die Datenstruktur. Jede Aufgabe wird als Objekt mit drei Eigenschaften gespeichert: einer eindeutigen ID (damit wir Aufgaben sicher identifizieren können), dem Text und dem Status (erledigt oder nicht). Alle Aufgaben werden in einem Array gehalten. Dieses Array ist die einzige Wahrheitsquelle (Single Source of Truth) – die Anzeige leitet sich immer daraus ab.

// Datenstruktur und Hilfsfunktionen
let aufgaben = JSON.parse(localStorage.getItem("aufgaben")) || [];

function speichern() {
  localStorage.setItem("aufgaben", JSON.stringify(aufgaben));
}

function neueId() {
  return Date.now().toString();
}

function aufgabeHinzufuegen(text) {
  if (text.trim() === "") return;
  aufgaben.push({
    id: neueId(),
    text: text.trim(),
    erledigt: false
  });
  speichern();
  rendern();
}

function aufgabeLoeschen(id) {
  aufgaben = aufgaben.filter(a => a.id !== id);
  speichern();
  rendern();
}

function aufgabeUmschalten(id) {
  const aufgabe = aufgaben.find(a => a.id === id);
  if (aufgabe) {
    aufgabe.erledigt = !aufgabe.erledigt;
    speichern();
    rendern();
  }
}

function erledigteLoeschen() {
  aufgaben = aufgaben.filter(a => !a.erledigt);
  speichern();
  rendern();
}

Die render-Funktion – UI aus Daten ableiten. Das wichtigste Konzept moderner Frontend-Entwicklung: Die Benutzeroberfläche ist nur eine Abbildung der Daten. Wenn sich die Daten ändern, wird die UI neu gezeichnet. Unsere rendern()-Funktion löscht die Liste und baut sie komplett aus dem aufgaben-Array neu auf. Das ist einfacher zu verstehen und weniger fehleranfällig als das Verfolgen einzelner Änderungen.

// Render-Funktion: Liste aus dem Array aufbauen
function rendern() {
  const liste = document.getElementById("aufgaben-liste");
  const offenZaehler = document.getElementById("offen-zaehler");

  liste.innerHTML = ""; // Liste leeren

  if (aufgaben.length === 0) {
    liste.innerHTML = "<li class='leer'>Keine Aufgaben – super!</li>";
  }

  aufgaben.forEach(aufgabe => {
    const li = document.createElement("li");
    li.className = aufgabe.erledigt ? "aufgabe aufgabe--erledigt" : "aufgabe";
    li.dataset.id = aufgabe.id;

    const checkbox = document.createElement("input");
    checkbox.type = "checkbox";
    checkbox.checked = aufgabe.erledigt;
    checkbox.className = "aufgabe-checkbox";

    const textSpan = document.createElement("span");
    textSpan.textContent = aufgabe.text;
    textSpan.className = "aufgabe-text";

    const loeschenBtn = document.createElement("button");
    loeschenBtn.textContent = "×";
    loeschenBtn.className = "loeschen-btn";

    li.appendChild(checkbox);
    li.appendChild(textSpan);
    li.appendChild(loeschenBtn);
    liste.appendChild(li);
  });

  // Zähler aktualisieren
  const offen = aufgaben.filter(a => !a.erledigt).length;
  offenZaehler.textContent = `${offen} offen`;
}

// Event-Listener einrichten
document.addEventListener("DOMContentLoaded", () => {
  rendern();

  const input = document.getElementById("aufgaben-input");
  const hinzBtn = document.getElementById("hinzufuegen-btn");
  const liste = document.getElementById("aufgaben-liste");
  const erledigtBtn = document.getElementById("erledigt-loeschen");

  // Hinzufügen per Button
  hinzBtn.addEventListener("click", () => {
    aufgabeHinzufuegen(input.value);
    input.value = "";
    input.focus();
  });

  // Hinzufügen per Enter-Taste
  input.addEventListener("keydown", (e) => {
    if (e.key === "Enter") {
      aufgabeHinzufuegen(input.value);
      input.value = "";
    }
  });

  // Event Delegation für Checkbox und Löschen-Button
  liste.addEventListener("click", (e) => {
    const li = e.target.closest("li");
    if (!li) return;
    const id = li.dataset.id;

    if (e.target.classList.contains("aufgabe-checkbox")) {
      aufgabeUmschalten(id);
    }
    if (e.target.classList.contains("loeschen-btn")) {
      aufgabeLoeschen(id);
    }
  });

  // Erledigte Aufgaben löschen
  erledigtBtn.addEventListener("click", erledigteLoeschen);
});
Übungsaufgaben
  1. Einfach: Baue die Todo-Liste nach dem obigen Beispiel nach. Teste, ob das Hinzufügen, Abhaken und Löschen von Aufgaben funktioniert. Lade die Seite neu – bleiben die Aufgaben erhalten (localStorage)?
  2. Mittel: Füge einen Filter hinzu: Drei Buttons "Alle", "Offen" und "Erledigt" sollen die Anzeige der Liste filtern. Implementiere eine filter-Variable, die die rendern()-Funktion berücksichtigt.
  3. Schwer: Implementiere die Möglichkeit, Aufgaben durch Doppelklick zu bearbeiten. Bei einem Doppelklick auf den Aufgabentext soll ein Eingabefeld erscheinen, in dem der Text geändert werden kann. Bei Drücken von Enter oder beim Verlassen des Feldes (blur-Event) soll der neue Text gespeichert werden.

Wichtige JavaScript-Befehle

Eine Übersicht der häufigsten JavaScript-Befehle als schnelle Referenz für den Alltag.

console.log()

Gibt Werte in der Browser-Konsole aus. Wichtigstes Debugging-Werkzeug.

console.log("Hallo!");
console.log(42, true, [1,2,3]);
console.warn("Warnung!");
console.error("Fehler!");

let / const

Variablen deklarieren. const für unveränderliche, let für veränderliche Werte.

const PI = 3.14159;
let counter = 0;
counter += 1;
// const PI = 99; // Fehler!

function

Wiederverwendbaren Code in einer benannten Funktion kapseln.

function addiere(a, b) {
  return a + b;
}
// Arrow Function:
const mult = (a, b) => a * b;

if / else

Bedingte Ausführung von Code. Nur ausführen wenn eine Bedingung wahr ist.

const alter = 20;
if (alter >= 18) {
  console.log("Erwachsen");
} else {
  console.log("Minderjährig");
}

for

Schleife mit bekannter Anzahl von Durchläufen. for...of für Arrays.

for (let i = 0; i < 5; i++) {
  console.log(i);
}
const arr = [1, 2, 3];
for (const x of arr) {
  console.log(x);
}

while

Schleife, die läuft, solange eine Bedingung wahr ist.

let n = 1;
while (n <= 10) {
  console.log(n);
  n++;
}
// Achtung: Endlosschleife vermeiden!

Array.push()

Element ans Ende eines Arrays anfügen. Gibt die neue Länge zurück.

const liste = ["a", "b"];
liste.push("c");
console.log(liste); // ["a","b","c"]
liste.pop();        // letztes entfernen

Array.map()

Neues Array erstellen, bei dem jedes Element transformiert wurde.

const zahlen = [1, 2, 3, 4];
const doppelt = zahlen.map(n => n * 2);
console.log(doppelt);
// [2, 4, 6, 8]

Array.filter()

Neues Array mit nur den Elementen, die eine Bedingung erfüllen.

const zahlen = [1, 2, 3, 4, 5, 6];
const gerade = zahlen.filter(n => n % 2 === 0);
console.log(gerade);
// [2, 4, 6]

document.querySelector()

Erstes HTML-Element finden, das einem CSS-Selektor entspricht.

const btn = document.querySelector("#btn");
const alle = document.querySelectorAll(".card");
btn.textContent = "Neu!";
btn.classList.add("aktiv");

addEventListener()

Auf ein Ereignis (Event) reagieren, z.B. Klick oder Tastatureingabe.

const btn = document.querySelector("button");
btn.addEventListener("click", (e) => {
  console.log("Geklickt!");
  console.log(e.target);
});

JSON.stringify() / parse()

Objekte in Strings umwandeln (z.B. für localStorage) und zurück.

const obj = { name: "Max", alter: 25 };
const str = JSON.stringify(obj);
// '{"name":"Max","alter":25}'
const back = JSON.parse(str);
console.log(back.name); // "Max"

Tipps zum Lernen

JavaScript zu lernen ist ein Prozess – du wirst nicht alles sofort verstehen, und das ist vollkommen normal. Diese Tipps helfen dir, schneller Fortschritte zu machen und häufige Frustrationen zu vermeiden.