Abschnitt 12 · Lektion 3
Die wichtigsten Ereignisse
Es gibt weit über hundert Ereignisse. Ein knappes Dutzend deckt fast alles ab, was man täglich braucht.
click ist der Allrounder
click feuert nicht nur bei einem Mausklick. Bei einem button löst auch die Eingabetaste und die Leertaste es aus, wenn das Element den Fokus hat.
Genau deshalb baut man Knöpfe als <button> und nicht als <div>. Ein div mit einem Klick-Listener sieht aus wie ein Knopf und ist keiner: Es bekommt keinen Fokus, die Tastatur erreicht es nicht, und ein Screenreader kündigt es nicht als bedienbar an. Wer es trotzdem tut, muss tabindex, role und eigene Tastatur-Behandlung nachbauen, und dann hat er ein schlechteres button. Lektion 20.4 nimmt das noch einmal auseinander.
dblclick gibt es auch, und man braucht es fast nie: Doppelklicks sind auf Touchgeräten kaum bedienbar.
input oder change
<p><input id="feld" type="text" placeholder="Tipp hier etwas"></p>
<p>
<select id="auswahl">
<option value="">Bitte wählen</option>
<option value="html">HTML</option>
<option value="js">JavaScript</option>
</select>
</p>
<pre id="protokoll"></pre> body {
font-family: system-ui, sans-serif;
max-width: 40rem;
margin: 0;
padding: 1rem;
line-height: 1.6;
color: #1c1917;
}
p {
margin: 0 0 0.75rem;
}
button,
input,
select,
textarea {
font: inherit;
}
input,
select,
textarea {
padding: 0.35rem 0.5rem;
border: 1px solid #d6d3d1;
border-radius: 6px;
} const protokoll = document.getElementById("protokoll");
function notiere(text) {
protokoll.textContent = `${protokoll.textContent}${text}\n`;
}
// input feuert bei JEDEM Zeichen, auch beim Einfügen
// mit der Maus und beim Löschen.
document.getElementById("feld").addEventListener("input", (event) => {
notiere(`input: "${event.target.value}"`);
});
// change feuert erst, wenn das Feld verlassen wird und
// sich dabei etwas geändert hat.
document.getElementById("feld").addEventListener("change", (event) => {
notiere(`change: "${event.target.value}"`);
});
// Bei einer Auswahlliste ist change der Normalfall: dort
// gibt es kein Tippen, nur ein Auswählen.
document.getElementById("auswahl").addEventListener("change", (event) => {
notiere(`Auswahl geändert auf "${event.target.value}"`);
});
console.log("Tipp ins Feld, klick dann daneben, und wähl etwas aus."); Diese Entscheidung merkt der Benutzer.
input feuert bei jeder Änderung, also bei jedem einzelnen Zeichen, beim Löschen und auch beim Einfügen mit der Maus. Für eine Suche, die live mitfiltert, ist das genau richtig.
change feuert erst, wenn das Feld verlassen wird und sich dabei etwas geändert hat. Für eine Eingabe, deren Prüfung erst am Ende sinnvoll ist, passt das besser: eine Meldung „ungültige Adresse”, während jemand noch tippt, ist nur lästig.
Bei einer Auswahlliste ist change der Normalfall. Dort wird nicht getippt, sondern ausgewählt, und dann ist die Änderung sofort fertig.
Beide feuern nur, wenn ein Mensch etwas tut. Setzt Code den Wert, bleibt es still: document.getElementById("feld").value = "Hallo" in der Eingabezeile der Console schreibt Hallo ins Feld, und im Protokoll darunter kommt keine Zeile dazu, weder input noch change. Wer ein Feld aus dem Code füllt und danach auf seinen Listener wartet, wartet vergebens.
keydown und die Tastennamen
<input id="feld" type="text" placeholder="Drück Tasten">
<p id="ausgabe">noch nichts</p> body {
font-family: system-ui, sans-serif;
max-width: 40rem;
margin: 0;
padding: 1rem;
line-height: 1.6;
color: #1c1917;
}
p {
margin: 0 0 0.75rem;
}
button,
input,
select,
textarea {
font: inherit;
}
input,
select,
textarea {
padding: 0.35rem 0.5rem;
border: 1px solid #d6d3d1;
border-radius: 6px;
} const ausgabe = document.getElementById("ausgabe");
document.getElementById("feld").addEventListener("keydown", (event) => {
ausgabe.textContent = `key: ${event.key}`;
console.log("key:", event.key);
// Auf eine bestimmte Taste reagieren: über den Namen.
if (event.key === "Enter") {
ausgabe.textContent = "Enter gedrückt";
}
if (event.key === "Escape") {
event.target.value = "";
ausgabe.textContent = "geleert";
}
});
// Die Namen sind Text und lesbar: "a", "A", "Enter",
// "Escape", "ArrowLeft", " " für die Leertaste.
console.log("Probier Buchstaben, Enter, Escape und die Pfeiltasten."); keydown feuert, sobald eine Taste gedrückt wird, und wiederholt sich, solange sie gehalten wird. keyup feuert beim Loslassen.
In event.key steht der Name der Taste als lesbarer Text: "a", "A", "Enter", "Escape", "ArrowLeft", und " " für die Leertaste. Damit fragt man direkt ab, ohne Zahlencodes nachzuschlagen.
Der alte event.keyCode mit seinen Nummern gilt als überholt und kommt hier nicht vor.
focus, blur und der Aufstieg
<div id="huelle">
<input id="feld" type="text" placeholder="Klick hinein und wieder heraus">
</div>
<pre id="protokoll"></pre> body {
font-family: system-ui, sans-serif;
max-width: 40rem;
margin: 0;
padding: 1rem;
line-height: 1.6;
color: #1c1917;
}
button,
input,
select,
textarea {
font: inherit;
}
input,
select,
textarea {
padding: 0.35rem 0.5rem;
border: 1px solid #d6d3d1;
border-radius: 6px;
}
#huelle {
padding: 12px;
background: #e2e8f0;
} const protokoll = document.getElementById("protokoll");
function notiere(text) {
protokoll.textContent = `${protokoll.textContent}${text}\n`;
}
const feld = document.getElementById("feld");
const huelle = document.getElementById("huelle");
feld.addEventListener("focus", () => notiere("Feld: focus"));
feld.addEventListener("blur", () => notiere("Feld: blur"));
// Am Elternteil kommt focus NICHT an: es steigt nicht auf.
huelle.addEventListener("focus", () => notiere("Hülle: focus"));
// Dafür gibt es focusin, und das steigt auf.
huelle.addEventListener("focusin", () => notiere("Hülle: focusin"));
// Dasselbe Paar bei der Maus: mouseenter steigt nicht auf,
// mouseover schon.
huelle.addEventListener("mouseenter", () => notiere("Hülle: mouseenter"));
huelle.addEventListener("mouseover", () => notiere("Hülle: mouseover"));
console.log("Klick ins Feld, dann daneben, und fahr mit der Maus darüber."); focus feuert, wenn ein Element den Fokus bekommt, blur, wenn es ihn verliert. Beides braucht man für Formulare ständig, etwa um erst beim Verlassen zu prüfen.
Diese beiden haben eine Eigenheit: sie steigen nicht auf. Ein Listener am Elternteil bekommt nichts mit. Warum das zählt, ist Thema von Lektion 12.5 und 12.6, hier reicht der Merksatz: Wo du den Aufstieg brauchst, nimm focusin und focusout.
Ein blur bekommst du hier auch ohne Klick daneben: Klick ins Feld, im Protokoll steht Feld: focus und Hülle: focusin. Wechsle dann zum Reiter Console, und im Protokoll steht Feld: blur, denn die Seite hat den Fokus an die Eingabezeile verloren. Deshalb antwortet document.activeElement dort immer mit <body> und document.hasFocus() mit false. Welches Element gerade den Fokus hat, fragst du im Code und nicht in der Eingabezeile.
Dasselbe Paar gibt es bei der Maus: mouseenter und mouseleave steigen nicht auf, mouseover und mouseout schon. Die ersten beiden feuern dafür nur beim tatsächlichen Betreten der Fläche und nicht bei jedem Wechsel zwischen Kindelementen, und das ist meistens das, was man will.
submit gehört ans Formular
submit feuert am <form>, nicht am Knopf. Das ist wichtig genug für eine eigene Lektion, und Abschnitt 13 macht daraus einen ganzen Abschnitt.
Vorab die Kurzfassung: Ein Listener am Absendeknopf verpasst die halbe Bedienung, denn die Eingabetaste im Feld schickt das Formular genauso ab.
Und DOMContentLoaded
DOMContentLoaded feuert am document, sobald das Markup fertig geladen ist. In vielen Anleitungen steht deshalb der gesamte Code darin eingepackt.
Mit defer aus Lektion 1.2 brauchst du das nicht. Ein Skript mit defer läuft ohnehin erst, wenn das Dokument steht, und dasselbe gilt für ein Modul. In diesem Kurs ist das Skript immer so eingebunden, deshalb kommt DOMContentLoaded hier nicht vor.
Wissen musst du es trotzdem, denn in fremdem Code steht es überall. Wenn du es siehst, ist es meistens ein Überbleibsel aus der Zeit vor defer.
Zum Mitnehmen
input feuert bei jedem Zeichen, change erst beim Verlassen des Feldes. Diese Entscheidung bestimmt, ob sich eine Suche lebendig oder träge anfühlt.
Jetzt du
Basis Konto, kostenlosZu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.
Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.
Was in dieser Lektion steckt
-
Artikel mit 3 Beispielen zum Ausprobieren
Steht hier, ohne Konto lesbar.
-
Aufgabe im Editor, direkt im Browser geprüft
Öffnet sich mit dem Basis Konto.