Wenn ein HTML-Formular abgeschickt wird, lädt normalerweise die Seite neu. Das ist vielleicht nicht immer die beste User-Experience, im Sinne von Progressive Enhancement aber der beste Ausgangspunkt.
Wenn wir für Benutzer*innen, die JavaScript aktiv haben (und bei denen das Laden und Verarbeiten der JS-Ressourcen geklappt hat) die Erfahrung verbessern wollen, bietet es sich in manchen Fällen an, das Formular per AJAX zu verschicken, sodass die Seite nicht neugeladen werden muss.
Update vom 2. September: Lars hat mich auf die Existenz von fetch()
aufmerksam gemacht, eine modernere Alternative zu XMLHttpRequest
für Requests. Ich habe den Artikel entsprechend angepasst.
Der erste Impuls könnte sein, beim submit
-Event des Formulars oder beim click
-Event des Submit-Buttons per document.querySelector(".input-selector").value
die Werte der einzelnen Formularfelder zu ermitteln und dann damit irgendwie einen AJAX-Request zusammenzubauen.
Es geht aber deutlich einfacher, und zwar mit der FormData
-Klasse. Damit sieht das Versenden eines Formulars per AJAX beispielhaft wie folgt aus:
(function() {
// IE does not support fetch().
if (!window.fetch) {
return;
}
const form = document.querySelector("form");
if (!form) {
return;
}
form.addEventListener("submit", async function(e) {
e.preventDefault();
const form = e.target,
data = new FormData(form),
method = form.method.toUpperCase(),
url = form.action;
const response = await fetch(url, {
method,
body: data,
headers: {
"X-Requested-With": "XMLHttpRequest"
}
});
if (response.ok === false) {
// Something wrong with the request.
return;
}
// Do something with the result (in response.json() for JSON responses).
});
})();
Code-Sprache: JavaScript (javascript)
Zunächst prüfen wir, ob der Browser fetch()
unterstützt (der Browser-Support für fetch()
ist gut) und speichern das erste form
-Element auf der Seite in der form
-Konstante. Nach einer Prüfung, ob überhaupt ein Formular vorhanden ist, hängen wir eine asynchrone Funktion an das submit
-Event des Formulars an (asynchron deswegen, um innerhalb der Funktion await
nutzen zu können).
In dieser Funktion verhindern wir mit e.preventDefault()
das normale Absenden des Formulars und das Neuladen der Seite und erstellen anschließend ein paar Konstanten:
form
bekommt das abgesendete Formular zugewiesen. An sich könnte hier auchform
von außerhalb der Funktion weitergenutzt werden, so funktioniert der Code aber auch, wenn der Event-Listener direkt an das Ergebnis vonquerySelector()
angehängt werden sollte.data
beinhaltet die Rückgabe desFormData
-Konstruktors, an den das gesamte Formular-Element übergeben wird. Das praktische: das können wir gleich als Daten für den Request nutzen, und haben damit alle Felder des Formulars im Request, die normalerweise auch verschickt werden würden.method
speichert die Methode des Formulars undurl
speichert die URL, an die die Daten geschickt werden sollen.
In Zeile 19 bis 25 schicken wir den Request an die URL aus dem action
-Attribut des Formulars ab und übergeben als zweiten Parameter ein Objekt mit zusätzlichen Daten für den Request, nämlich:
- die Art des Requests
method
(da der Wert dafür in der gleichnamigen Konstantemethod
gespeichert ist, genügt es,method
zu übergeben stattmethod: method
), - in
body
die Daten des Formulars, - (optional) den
X-Requested-With
-Header, damit etwa Laravels$request->ajax()
-Methode erkennt, dass es sich um einen AJAX-Request handelt. Hier nutzen wir als WertXMLHttpRequest
, etwas wiefetch
hat bei mir nicht funktioniert.
Durch Nutzung von await
wird der Code danach erst ausgeführt, wenn wir eine Antwort auf den Request haben. Dann prüfen wir über response.ok
ob der Status-Code der Antwort im 200er-Bereich liegt und können Dinge mit den Daten anstellen, die wir zurückbekommen haben.
Und das war’s auch schon, wir verschicken damit das Formular ohne Neuladen der Seite via AJAX.
Warum hast du XMLHttpRequest anstatt fetch (developer.mozilla.org/en-US/docs/Web…) verwendet? fetch ist doch wesentlich geschmeidiger zu verwenden (=> in deinem Beispiel : submit handler als async function schreiben, dann await fetch(...) und ab dafür ... 😉
Cool! fetch kannte ich nicht, danke für den Tipp! Schaue ich mir morgen mal an und aktualisiere das dann 🙂
Hi! Alles schön schlank und größtenteils für Anfänger wie mich verständlich, großen Dank! Aber noch eine Frage: wie binde ich ein bestimmtes von mehreren Formularen auf einer Seite ein? Der querySelector 'form' greift sich ja wohl immer das erste...mit getElByID klappt es auch nicht...hast du Rat? Und noch ne Kleinigkeit: "form gleich e.target" ... wofür steht 'target'? Danke!
Joachim
Hi,
du kannst den Wert in
querySelector
auf jeden beliebigen CSS-Selektor anpassen. Wenn deinform
-Element beispielsweise die Klassespecial-form
hat, könntest duquerySelector( '.special-form' );
schreiben.Das
target
ist das Element, für den der Event-Listener gefeuert wurde, also im Beispiel das Formular.Viele Grüße