mitmario.dev

Synthese: das geprüfte Formular

JavaScript Im Browser 3 Min Lesezeit 3 BeispieleLektion 5 von 6

Alles aus diesem Abschnitt in einem Stück: lesen, abfangen, prüfen, melden, einsammeln und den Zustand des Knopfes führen.

Die Reihenfolge

Die Reihenfolge im Handler
const formular = document.getElementById("formular");
const felder = [...formular.querySelectorAll("input")];

function melde(feld, meldung) {
  document.getElementById(`fehler-${feld.id}`).textContent = meldung;
  feld.setAttribute("aria-invalid", meldung !== "" ? "true" : "false");
}

function pruefe(feld) {
  const wert = feld.value.trim();
  if (wert === "") return "Bitte ausfüllen";
  if (feld.id === "email" && !wert.includes("@")) return "Da fehlt ein @";
  return "";
}

formular.addEventListener("submit", (event) => {
  // 1. Abfangen. Immer als Erstes.
  event.preventDefault();

  // 2. Prüfen und melden, und zwar ALLE Felder. Nicht beim
  //    ersten Fehler aufhören: der Benutzer will alles auf
  //    einmal sehen und nicht dreimal hintereinander.
  const meldungen = felder.map((feld) => {
    const meldung = pruefe(feld);
    melde(feld, meldung);
    return meldung;
  });

  // 3. War etwas dabei, hier aufhören.
  if (meldungen.some((meldung) => meldung !== "")) {
    document.getElementById("status").textContent = "Bitte sieh dir die Meldungen an";
    return;
  }

  // 4. Erst jetzt weiterreichen.
  document.getElementById("status").textContent = "Alles in Ordnung";
  console.log("hier ginge es zum Server, siehe Abschnitt 16");
});

console.log("Schick leer ab, füll aus, schick wieder ab.");

Ein submit-Handler hat immer dieselbe Gestalt, und die Reihenfolge darin ist keine Geschmacksfrage.

Erst abfangen. preventDefault() ganz vorn, bevor irgendetwas schiefgehen kann. Steht es weiter unten und der Code darüber wirft einen Fehler, lädt die Seite neu und nimmt die Fehlermeldung gleich mit.

Dann alle Felder prüfen und melden. Betonung auf alle: Nicht beim ersten Fehler aufhören. Wer drei Felder falsch ausgefüllt hat, will das einmal erfahren und nicht dreimal hintereinander.

Dann entscheiden. War eine Meldung dabei, hört der Handler hier auf.

Erst dann weiterreichen. In diesem Kurs heißt das: das Objekt bauen und irgendwo hinlegen. Was hier fehlt, ist das Verschicken, und das ist Abschnitt 16. Absichtlich: Ein POST braucht einen Endpunkt mit Rate-Limit und Prüfung auf der Gegenseite, und das gehört nicht in eine Übung.

Der Knopf

Den Knopf sperren und wieder freigeben
const status = document.getElementById("status");

function speichere(fertig) {
  setTimeout(fertig, 800);
}

// So: gesperrt, und im Rückruf wieder frei.
document.getElementById("sauber").addEventListener("submit", (event) => {
  event.preventDefault();
  const knopf = event.currentTarget.querySelector("button");

  knopf.disabled = true;
  status.textContent = "Speichert";

  speichere(() => {
    knopf.disabled = false;
    status.textContent = "Gespeichert";
  });
});

// So nicht: der Knopf wird gesperrt und nie wieder frei,
// weil das Freigeben nur im Erfolgsfall steht. Hier ist der
// Erfolgsfall einfach nicht eingebaut.
document.getElementById("kaputt").addEventListener("submit", (event) => {
  event.preventDefault();
  event.currentTarget.querySelector("button").disabled = true;
  status.textContent = "Speichert und bleibt dabei";
});

console.log("Schick beide ab und versuch dann, sie noch einmal abzuschicken.");

Solange eine Anfrage läuft, gehört der Absendeknopf gesperrt. Der Grund ist banal und passiert ständig: Wer nichts passieren sieht, drückt noch einmal. Und dann steht die Bestellung zweimal da.

speichere steht im Beispiel für so eine Anfrage. Das setTimeout darin sorgt nur dafür, dass fertig erst nach 800 Millisekunden aufgerufen wird, so wie eine echte Antwort vom Server auch nicht sofort da ist. Wie das genau funktioniert, ist Abschnitt 15; hier zählt nur, dass zwischen Sperren und Freigeben Zeit vergeht.

Zum Sperren gehört das Freigeben, und zwar auf jedem Weg. Der Erfolgsfall ist der leichte. Der Fehlerfall ist der, der vergessen wird, und dann bleibt der Knopf für immer gesperrt: Die Seite sieht aus, als arbeite sie noch, und der Benutzer kann nichts mehr tun außer neu laden.

Im zweiten Beispiel stehen beide Fassungen nebeneinander. Die zweite sperrt und gibt nie frei.

Abschnitt 14 hat das passende Werkzeug dafür: finally läuft, egal wie der Block ausgegangen ist. Bis dahin gilt: Wer sperrt, schreibt das Freigeben im selben Atemzug dazu.

Die Erfolgsmeldung

Die Erfolgsmeldung
const status = document.getElementById("status");

document.getElementById("formular").addEventListener("submit", (event) => {
  event.preventDefault();
  const adresse = new FormData(event.currentTarget).get("email");

  // Gut: sagt, was passiert ist, mit dem konkreten Wert,
  // und sagt, was als Nächstes kommt.
  status.textContent = `Eingetragen: ${adresse}. Du bekommst gleich eine Bestätigung.`;

  // Weniger gut wären: "Erfolg!", "OK" oder gar nichts.
  console.log("Der Bereich trägt role=\"status\" und wird deshalb vorgelesen.");
});

// Der Bereich steht schon im Markup, auch solange er leer
// ist. Ein Element, das erst mit der Meldung entsteht, wird
// von einem Screenreader oft gar nicht angekündigt.
console.log("Schick das Formular ab.");

Eine gute Erfolgsmeldung leistet drei Dinge.

Sie sagt, was passiert ist, und zwar mit dem konkreten Wert: „Eingetragen: ida@example.de” statt „Erfolg”. Damit sieht der Benutzer nebenbei, ob er sich vertippt hat.

Sie sagt, was als Nächstes kommt. „Du bekommst gleich eine Bestätigung” beantwortet die Frage, die sonst offen bleibt.

Sie steht dort, wo man hinschaut, also beim Formular und nicht am Seitenende.

Dazu kommt ein Detail für Screenreader: Der Meldungsbereich steht schon im Markup, auch solange er leer ist, und trägt role="status". Ein Element, das erst zusammen mit der Meldung entsteht, wird oft gar nicht angekündigt. Lektion 20.4 nimmt das genauer auseinander.

Was jetzt fehlt

Das Formular prüft, meldet, sammelt ein und führt seinen Knopfzustand. Es verschickt nichts.

Genau zwei Abschnitte fehlen dafür. Abschnitt 15 erklärt, was der Rückruf im zweiten Beispiel eigentlich ist und warum Code nicht immer von oben nach unten läuft. Abschnitt 16 ersetzt das gespielte Speichern durch ein echtes fetch.

Der Rest bleibt genau so stehen, wie er hier gebaut wird.

Zum Mitnehmen

Abfangen, einsammeln, prüfen, melden oder weiterreichen. Immer in dieser Reihenfolge, und der Absendeknopf wird auf jedem Weg wieder freigegeben.

Jetzt du

Basis Konto, kostenlos

Zu 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.