Abschnitt 13 · Lektion 4
FormData
Felder einzeln auszulesen ist bei drei Feldern in Ordnung und bei fünfzehn eine Fleißarbeit. Dafür gibt es einen Abkürzer, der genau das tut, was der Browser beim Absenden ohnehin täte.
Ein Aufruf für alles
<form id="formular">
<input name="name" value="Ida">
<input name="alter" type="number" value="34">
<select name="thema">
<option value="js" selected>JavaScript</option>
<option value="css">CSS</option>
</select>
<button type="submit">Absenden</button>
</form>
<pre id="ausgabe"></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;
}
button {
padding: 0.4rem 0.8rem;
border: 1px solid #d6d3d1;
border-radius: 6px;
background: #fafaf9;
cursor: pointer;
}
input,
select,
textarea {
padding: 0.35rem 0.5rem;
border: 1px solid #d6d3d1;
border-radius: 6px;
} const formular = document.getElementById("formular");
formular.addEventListener("submit", (event) => {
event.preventDefault();
// Ein Aufruf, und alle Felder mit name sind drin.
const daten = new FormData(formular);
// get holt einen Wert, und es ist immer Text.
console.log("name:", daten.get("name"));
console.log("alter:", daten.get("alter"), "| Typ:", typeof daten.get("alter"));
// Durchlaufen geht wie über eine Liste von Paaren.
const zeilen = [];
for (const [schluessel, wert] of daten) {
zeilen.push(`${schluessel} = ${wert}`);
}
document.getElementById("ausgabe").textContent = zeilen.join("\n");
console.log("Anzahl Einträge:", [...daten.keys()].length);
});
console.log("Schick das Formular ab."); new FormData(formular) liest das ganze Formular ein. Was du bekommst, ist eine Sammlung von Paaren aus Schlüssel und Wert.
daten.get("name") holt einen Wert. daten.has("newsletter") fragt, ob ein Schlüssel überhaupt da ist. Und über die Paare kann man laufen wie über eine Liste, mit dem Destructuring aus Lektion 7.5.
Auch hier ist alles Text. Ein type="number" ändert daran nichts, genau wie bei value in Lektion 13.1.
Was drin ist, siehst du in der Eingabezeile der Console, allerdings nicht mit new FormData(formular) allein: Das antwortet FormData {}, geerbte Werte wie beim Event in Lektion 12.2. [...new FormData(formular)] macht die Paare zu einer Liste: [["name", "Ida"], ["alter", "34"], ["thema", "js"]]. Und Object.fromEntries(new FormData(formular)) antwortet {name: "Ida", alter: "34", thema: "js"}. Beide Male steht "34" in Anführungszeichen, und das ist der Satz von oben in Zeichen.
Was nicht dabei ist
<form id="formular">
<input name="mit-name" value="wird eingesammelt">
<input value="ohne name-Attribut">
<input name="haken" type="checkbox" value="ja">
<input name="gesetzt" type="checkbox" value="ja" checked>
<input name="gesperrt" value="gesperrt" disabled>
<button type="submit">Absenden</button>
</form>
<pre id="ausgabe"></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;
}
button {
padding: 0.4rem 0.8rem;
border: 1px solid #d6d3d1;
border-radius: 6px;
background: #fafaf9;
cursor: pointer;
}
input,
select,
textarea {
padding: 0.35rem 0.5rem;
border: 1px solid #d6d3d1;
border-radius: 6px;
} document.getElementById("formular").addEventListener("submit", (event) => {
event.preventDefault();
const daten = new FormData(event.currentTarget);
document.getElementById("ausgabe").textContent = [...daten.keys()].join("\n");
// Ohne name: nicht dabei. Das ist kein Fehler, sondern die Regel.
console.log("Schlüssel:", [...daten.keys()]);
// Ein nicht angehaktes Kästchen fehlt einfach. Es steht
// nicht als false darin, es ist gar nicht da.
console.log("haken vorhanden?", daten.has("haken"));
console.log("haken get:", daten.get("haken"));
// Das angehakte ist da, und zwar mit seinem value.
console.log("gesetzt:", daten.get("gesetzt"));
// Ein gesperrtes Feld zählt ebenfalls nicht mit.
console.log("gesperrt vorhanden?", daten.has("gesperrt"));
// Deshalb fragt man bei Kästchen has() und nicht get().
console.log("als Wahrheitswert:", daten.has("haken"), daten.has("gesetzt"));
});
console.log("Schick ab, hak um, schick wieder ab."); Drei Sorten Feld fehlen in einem FormData, und alle drei überraschen mindestens einmal.
Felder ohne name. Die Betonung liegt auf name, nicht auf id. Ein Feld ohne name-Attribut ist für das Formular nicht vorhanden, so sichtbar es auch sein mag. Das ist kein Fehler in FormData, sondern die Regel, nach der Formulare seit jeher funktionieren.
Nicht angehakte Kästchen. Und zwar richtig: Der Schlüssel ist nicht etwa auf false gesetzt, es gibt ihn schlicht nicht. daten.get("haken") liefert null.
Daraus folgt der praktische Umgang: Bei einem Ankreuzfeld fragst du has, nicht get. daten.has("newsletter") ist der Wahrheitswert, den du wolltest.
Gesperrte Felder. Ein disabled schließt das Feld vom Absenden aus, also auch von FormData.
Keine dieser drei Regeln gehört FormData. Es sind die Regeln des Formulars selbst, und du kannst sie nebeneinanderlegen: Lass ein Formular einmal ohne preventDefault abschicken und sieh im Reiter Netzwerk nach, was der Browser daraus gebaut hat. Dort stehen genau dieselben Paare. Das Feld ohne name fehlt, das nicht angehakte Kästchen fehlt, und ein angehaktes ohne eigenen value steht mit on da, hier wie dort. FormData ist kein zweiter Weg, es ist derselbe Weg, nur bevor die Anfrage rausgeht.
In ein Objekt umwandeln
<form id="formular">
<input name="name" value="Ida">
<input name="alter" type="number" value="34">
<input name="tag" type="checkbox" value="mo" checked>
<input name="tag" type="checkbox" value="di" checked>
<input name="tag" type="checkbox" value="mi">
<button type="submit">Absenden</button>
</form>
<pre id="ausgabe"></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;
}
button {
padding: 0.4rem 0.8rem;
border: 1px solid #d6d3d1;
border-radius: 6px;
background: #fafaf9;
cursor: pointer;
}
input,
select,
textarea {
padding: 0.35rem 0.5rem;
border: 1px solid #d6d3d1;
border-radius: 6px;
} document.getElementById("formular").addEventListener("submit", (event) => {
event.preventDefault();
const daten = new FormData(event.currentTarget);
// Object.fromEntries macht aus den Paaren ein Objekt.
// Bequem, und es hat zwei Haken.
const roh = Object.fromEntries(daten);
// JSON.stringify schreibt das Objekt in einer Zeile als Text,
// das Format dahinter erklärt Abschnitt 16.
console.log("roh:", JSON.stringify(roh));
// Erstens: alles ist Text, auch das Alter.
console.log("Typ von alter:", typeof roh.alter);
// Zweitens: bei mehrfach vergebenem name bleibt nur der
// letzte übrig. Dafür gibt es getAll.
console.log("alle Tage:", daten.getAll("tag"));
// Also von Hand, mit den richtigen Typen:
const sauber = {
name: daten.get("name"),
alter: Number(daten.get("alter")),
tage: daten.getAll("tag"),
};
document.getElementById("ausgabe").textContent = JSON.stringify(sauber, null, 2);
});
console.log("Schick ab und sieh dir beide Fassungen an."); Object.fromEntries(daten) macht in einer Zeile ein normales Objekt daraus. Das ist bequem und hat zwei Haken.
Erstens sind alle Werte Text. Ein Objekt mit alter: "34" sieht fertig aus und ist es nicht: Beim nächsten Rechnen wird daraus eine Zeichenkette.
Zweitens verliert es Mehrfachwerte. Kommt ein name mehrmals vor, etwa bei einer Gruppe von Kästchen, bleibt nur der letzte übrig. Dafür gibt es getAll, das eine Liste zurückgibt.
Deshalb steht im Kurs der ausgeschriebene Weg: ein Objekt, das Feld für Feld gebaut wird, mit Number wo gerechnet wird, has bei Kästchen und getAll bei Gruppen. Das sind ein paar Zeilen mehr, und sie sind die einzige Stelle, an der die Typen festgelegt werden.
Und wozu das Ganze
In Abschnitt 16 wird dieses Objekt verschickt. fetch nimmt ein FormData direkt entgegen, und für eine JSON-Anfrage ist das saubere Objekt genau das, was hineingehört.
Bis dahin ist es der Zustand aus Lektion 12.8, nur eben aus einem Formular gefüllt statt aus Klicks.
Zum Mitnehmen
FormData sammelt nur Felder mit einem name-Attribut ein. Ein Feld ohne name ist für das Formular nicht vorhanden, egal wie sichtbar es ist.
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.