Event Handling in JavaScript
Event Handling in JavaScript
Events machen Webseiten interaktiv. Jeder Klick, jede Tastatureingabe, jedes Scrollen - alles sind Events, auf die du reagieren kannst.
Events verstehen
Ein Event ist eine Aktion oder ein Vorkommnis:
- Benutzer klickt einen Button
- Benutzer tippt in ein Eingabefeld
- Seite ist fertig geladen
- Maus bewegt sich über ein Element
- Formular wird abgeschickt
Event Listener hinzufügen
addEventListener - Der Standard
const button = document.querySelector("#my-button");
// Event Listener hinzufügen
button.addEventListener("click", function(event) {
console.log("Button wurde geklickt!");
console.log("Event-Objekt:", event);
});
// Mit Arrow Function
button.addEventListener("click", (e) => {
console.log("Geklickt!");
});
// Benannte Funktion (für removeEventListener)
function handleClick(e) {
console.log("Geklickt!");
}
button.addEventListener("click", handleClick);
Event Listener entfernen
function handleClick(e) {
console.log("Geklickt!");
}
// Hinzufügen
button.addEventListener("click", handleClick);
// Entfernen - MUSS dieselbe Funktion sein!
button.removeEventListener("click", handleClick);
// Das funktioniert NICHT:
button.addEventListener("click", () => console.log("Hi"));
button.removeEventListener("click", () => console.log("Hi")); // Andere Funktion!
Optionen
// Einmal ausführen
button.addEventListener("click", () => {
console.log("Nur einmal!");
}, { once: true });
// Capture Phase (statt Bubble)
element.addEventListener("click", handler, { capture: true });
// oder kurz:
element.addEventListener("click", handler, true);
// Passive (Performance für scroll/touch)
window.addEventListener("scroll", handler, { passive: true });
Das Event-Objekt
Jeder Event Handler bekommt ein Event-Objekt:
button.addEventListener("click", (event) => {
// Allgemeine Eigenschaften
console.log(event.type); // "click"
console.log(event.target); // Das geklickte Element
console.log(event.currentTarget); // Das Element mit dem Listener
console.log(event.timeStamp); // Zeitpunkt
// Maus-Position
console.log(event.clientX, event.clientY); // Im Viewport
console.log(event.pageX, event.pageY); // Im Dokument
// Modifier-Tasten
console.log(event.ctrlKey); // Ctrl gedrückt?
console.log(event.shiftKey); // Shift gedrückt?
console.log(event.altKey); // Alt gedrückt?
console.log(event.metaKey); // Cmd/Win gedrückt?
});
Häufige Event-Typen
Maus-Events
const element = document.querySelector(".box");
// Klick
element.addEventListener("click", (e) => {
console.log("Klick!");
});
// Doppelklick
element.addEventListener("dblclick", (e) => {
console.log("Doppelklick!");
});
// Maus rein/raus
element.addEventListener("mouseenter", (e) => {
console.log("Maus rein");
});
element.addEventListener("mouseleave", (e) => {
console.log("Maus raus");
});
// Maus bewegt sich
element.addEventListener("mousemove", (e) => {
console.log(`Position: ${e.clientX}, ${e.clientY}`);
});
// Maus-Tasten
element.addEventListener("mousedown", (e) => {
console.log("Taste gedrückt");
});
element.addEventListener("mouseup", (e) => {
console.log("Taste losgelassen");
});
// Rechtsklick
element.addEventListener("contextmenu", (e) => {
e.preventDefault(); // Standard-Menü verhindern
console.log("Rechtsklick!");
});
Tastatur-Events
const input = document.querySelector("input");
// Taste gedrückt (wiederholt bei gedrückt halten)
input.addEventListener("keydown", (e) => {
console.log("Taste:", e.key); // "a", "Enter", "Escape"
console.log("Code:", e.code); // "KeyA", "Enter", "Escape"
// Bestimmte Taste prüfen
if (e.key === "Enter") {
console.log("Enter gedrückt!");
}
if (e.key === "Escape") {
console.log("Escape gedrückt!");
}
// Kombination prüfen
if (e.ctrlKey && e.key === "s") {
e.preventDefault(); // Browser-Speichern verhindern
console.log("Strg+S gedrückt!");
}
});
// Taste losgelassen
input.addEventListener("keyup", (e) => {
console.log("Losgelassen:", e.key);
});
Formular-Events
const form = document.querySelector("form");
const input = document.querySelector("input");
const select = document.querySelector("select");
// Formular abschicken
form.addEventListener("submit", (e) => {
e.preventDefault(); // Seiten-Reload verhindern!
console.log("Formular abgeschickt");
// Formular-Daten holen
const formData = new FormData(form);
console.log(formData.get("email"));
});
// Input-Wert ändert sich (bei jedem Zeichen)
input.addEventListener("input", (e) => {
console.log("Aktueller Wert:", e.target.value);
});
// Wert geändert (bei Verlassen des Feldes)
input.addEventListener("change", (e) => {
console.log("Neuer Wert:", e.target.value);
});
// Fokus
input.addEventListener("focus", () => {
console.log("Input hat Fokus");
});
input.addEventListener("blur", () => {
console.log("Input hat Fokus verloren");
});
// Select-Änderung
select.addEventListener("change", (e) => {
console.log("Ausgewählt:", e.target.value);
});
Fenster-Events
// Seite geladen
window.addEventListener("load", () => {
console.log("Alles geladen (inkl. Bilder)");
});
// DOM bereit (schneller als load)
document.addEventListener("DOMContentLoaded", () => {
console.log("DOM bereit");
});
// Fenstergröße geändert
window.addEventListener("resize", () => {
console.log(`Neue Größe: ${window.innerWidth}x${window.innerHeight}`);
});
// Gescrollt
window.addEventListener("scroll", () => {
console.log("Scroll-Position:", window.scrollY);
});
// Seite verlassen (Warnung zeigen)
window.addEventListener("beforeunload", (e) => {
e.preventDefault();
e.returnValue = ""; // Browser-Dialog
});
Event Propagation (Bubbling & Capturing)
Events “wandern” durch den DOM:
<div id="outer">
<div id="inner">
<button id="button">Klick</button>
</div>
</div>
// Alle drei reagieren auf den Button-Klick!
document.querySelector("#outer").addEventListener("click", () => {
console.log("Outer");
});
document.querySelector("#inner").addEventListener("click", () => {
console.log("Inner");
});
document.querySelector("#button").addEventListener("click", () => {
console.log("Button");
});
// Ausgabe bei Klick auf Button:
// "Button"
// "Inner"
// "Outer"
// (Event "bubbelt" nach oben)
stopPropagation - Bubbling stoppen
document.querySelector("#button").addEventListener("click", (e) => {
e.stopPropagation(); // Nicht weitergeben!
console.log("Button");
});
// Jetzt nur:
// "Button"
// (Outer und Inner werden nicht ausgeführt)
Capturing Phase
// Mit { capture: true } oder true als 3. Parameter
document.querySelector("#outer").addEventListener("click", () => {
console.log("Outer Capture");
}, true);
// Reihenfolge jetzt:
// 1. Capturing (von oben nach unten)
// 2. Target
// 3. Bubbling (von unten nach oben)
Event Delegation
Statt an jedes Element einzeln, an den Container hängen:
// Schlecht: Listener an jedes Item
document.querySelectorAll(".item").forEach(item => {
item.addEventListener("click", () => {
console.log("Item geklickt");
});
});
// Problem: Neue Items haben keinen Listener!
// Gut: Event Delegation
document.querySelector(".item-list").addEventListener("click", (e) => {
// Prüfen ob ein Item geklickt wurde
const item = e.target.closest(".item");
if (item) {
console.log("Item geklickt:", item);
}
});
// Funktioniert auch für neue Items!
Praktisches Beispiel: Todo-Liste mit Delegation
const todoList = document.querySelector("#todo-list");
// Ein Listener für alles!
todoList.addEventListener("click", (e) => {
const target = e.target;
// Delete-Button geklickt?
if (target.classList.contains("delete-btn")) {
const todoItem = target.closest(".todo-item");
todoItem.remove();
return;
}
// Checkbox geklickt?
if (target.classList.contains("todo-checkbox")) {
const todoItem = target.closest(".todo-item");
todoItem.classList.toggle("completed");
return;
}
// Todo-Text geklickt?
if (target.classList.contains("todo-text")) {
// Bearbeiten starten
startEditing(target);
}
});
preventDefault - Standard verhindern
// Link-Navigation verhindern
document.querySelector("a").addEventListener("click", (e) => {
e.preventDefault();
console.log("Link geklickt, aber nicht navigiert");
});
// Formular-Submit verhindern
document.querySelector("form").addEventListener("submit", (e) => {
e.preventDefault();
console.log("Formular nicht abgeschickt");
});
// Rechtsklick-Menü verhindern
document.addEventListener("contextmenu", (e) => {
e.preventDefault();
});
// Bestimmte Tastenkombinationen verhindern
document.addEventListener("keydown", (e) => {
if (e.ctrlKey && e.key === "s") {
e.preventDefault();
saveDocument(); // Eigene Speicher-Funktion
}
});
Praktische Beispiele
Drag & Drop (vereinfacht)
const draggable = document.querySelector(".draggable");
let isDragging = false;
let offsetX, offsetY;
draggable.addEventListener("mousedown", (e) => {
isDragging = true;
offsetX = e.clientX - draggable.offsetLeft;
offsetY = e.clientY - draggable.offsetTop;
draggable.style.cursor = "grabbing";
});
document.addEventListener("mousemove", (e) => {
if (!isDragging) return;
draggable.style.left = (e.clientX - offsetX) + "px";
draggable.style.top = (e.clientY - offsetY) + "px";
});
document.addEventListener("mouseup", () => {
isDragging = false;
draggable.style.cursor = "grab";
});
Infinite Scroll
window.addEventListener("scroll", () => {
// Prüfen ob am Ende der Seite
const scrollTop = window.scrollY;
const windowHeight = window.innerHeight;
const documentHeight = document.documentElement.scrollHeight;
if (scrollTop + windowHeight >= documentHeight - 100) {
loadMoreContent();
}
});
function loadMoreContent() {
// Neue Inhalte laden...
console.log("Lade mehr...");
}
Keyboard Navigation
const items = document.querySelectorAll(".menu-item");
let currentIndex = 0;
document.addEventListener("keydown", (e) => {
if (e.key === "ArrowDown") {
e.preventDefault();
currentIndex = Math.min(currentIndex + 1, items.length - 1);
items[currentIndex].focus();
}
if (e.key === "ArrowUp") {
e.preventDefault();
currentIndex = Math.max(currentIndex - 1, 0);
items[currentIndex].focus();
}
if (e.key === "Enter") {
items[currentIndex].click();
}
});
Debounce für Performance
function debounce(fn, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn.apply(this, args), delay);
};
}
// Ohne Debounce - bei jedem Zeichen
input.addEventListener("input", (e) => {
search(e.target.value); // Zu viele Aufrufe!
});
// Mit Debounce - 300ms nach letzter Eingabe
input.addEventListener("input", debounce((e) => {
search(e.target.value);
}, 300));
Throttle für Scroll
function throttle(fn, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
fn.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
// Max alle 100ms ausführen
window.addEventListener("scroll", throttle(() => {
console.log("Scroll:", window.scrollY);
}, 100));
Custom Events
// Custom Event erstellen
const myEvent = new CustomEvent("userLoggedIn", {
detail: {
userId: 123,
username: "max"
},
bubbles: true
});
// Event dispatchen
document.dispatchEvent(myEvent);
// Event hören
document.addEventListener("userLoggedIn", (e) => {
console.log("User eingeloggt:", e.detail.username);
});
Zusammenfassung
| Event | Beschreibung |
|---|---|
click | Mausklick |
dblclick | Doppelklick |
mouseenter/leave | Maus rein/raus |
keydown/keyup | Taste drücken/loslassen |
input | Wert ändert sich |
change | Wert geändert (bei Blur) |
submit | Formular abschicken |
focus/blur | Fokus erhalten/verlieren |
scroll | Scrollen |
resize | Fenstergröße |
// Merksatz:
element.addEventListener("eventType", (event) => {
event.preventDefault(); // Standard verhindern
event.stopPropagation(); // Bubbling stoppen
event.target; // Geklicktes Element
event.currentTarget; // Element mit Listener
});
Übung: Baue eine Bildergalerie mit Keyboard-Navigation: Pfeiltasten zum Navigieren, Escape zum Schließen, und Klick auf Thumbnail zum Öffnen.