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:
formbekommt das abgesendete Formular zugewiesen. An sich könnte hier auchformvon 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.databeinhaltet 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.methodspeichert die Methode des Formulars undurlspeichert 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 Konstantemethodgespeichert ist, genügt es,methodzu übergeben stattmethod: method), - in
bodydie 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 wiefetchhat 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
querySelectorauf jeden beliebigen CSS-Selektor anpassen. Wenn deinform-Element beispielsweise die Klassespecial-formhat, könntest duquerySelector( '.special-form' );schreiben.Das
targetist das Element, für den der Event-Listener gefeuert wurde, also im Beispiel das Formular.Viele Grüße