Signatur
Beschreibung
Das Ereignis loadend ist das letzte Fortschrittsereignis im Lebenszyklus einer XMLHttpRequest-Anfrage. Es wird immer ausgelöst, sobald die Anfrage abgeschlossen ist – unabhängig davon, ob davor load, error, abort oder timeout gefeuert wurde. Damit eignet es sich ideal für Aufräumarbeiten, vergleichbar mit einem finally-Block.
Typische Einsatzzwecke sind das Ausblenden von Lade-Spinnern, das Wieder-Aktivieren von Buttons, das Zurücksetzen von Fortschrittsbalken oder das Freigeben von Ressourcen. Den Handler registriert man über addEventListener("loadend", handler) oder über die Eigenschaft onloadend.
Da XMLHttpRequestEventTarget sowohl von XMLHttpRequest als auch von XMLHttpRequestUpload (xhr.upload) implementiert wird, gibt es das Ereignis auf beiden Objekten: Auf xhr.upload signalisiert es das Ende der Übertragung des Anfragekörpers, auf xhr selbst das Ende der gesamten Anfrage inklusive Antwort.
Der Handler erhält ein ProgressEvent mit den Eigenschaften loaded, total und lengthComputable. Ob die Anfrage erfolgreich war, verrät das Ereignis selbst nicht – dafür prüft man xhr.status bzw. merkt sich, welches vorherige Ereignis ausgelöst wurde.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $type Pflicht | string | Der Ereignisname "loadend". |
|
| $handler Pflicht | (event: ProgressEvent) => void | Callback, der ein ProgressEvent mit loaded, total und lengthComputable erhält. |
|
| $options | boolean | AddEventListenerOptions | false | Optionen wie once, signal oder capture. |
Rückgabewert
addEventListener gibt nichts zurück. Der Handler bekommt ein ProgressEvent-Objekt.Beispiele
Lade-Spinner immer ausblenden (addEventListener)
const spinner = document.querySelector('#spinner');
const xhr = new XMLHttpRequest();
xhr.addEventListener('load', () => console.log('Status:', xhr.status));
xhr.addEventListener('error', () => console.log('Netzwerkfehler'));
xhr.addEventListener('loadend', (event) => {
// Wird in jedem Fall ausgelöst – wie finally
spinner.hidden = true;
console.log(`Fertig: ${event.loaded} Bytes empfangen`);
});
spinner.hidden = false;
xhr.open('GET', '/api/users');
xhr.send();
onloadend-Property und Erfolg über status prüfen
const xhr = new XMLHttpRequest();
xhr.timeout = 5000;
xhr.onloadend = () => {
if (xhr.status >= 200 && xhr.status < 300) {
console.log('Erfolg:', xhr.responseText.length, 'Zeichen');
} else if (xhr.status === 0) {
console.log('Abgebrochen, Timeout oder Netzwerkfehler');
} else {
console.log('HTTP-Fehler:', xhr.status);
}
};
xhr.open('GET', '/api/report');
xhr.send();
Upload-Ende und Listener wieder entfernen
const button = document.querySelector('#upload');
const input = document.querySelector('#file');
const sendFile = () => {
const xhr = new XMLHttpRequest();
const onUploadEnd = (e) => {
console.log(`Upload beendet: ${e.loaded}/${e.total} Bytes`);
xhr.upload.removeEventListener('loadend', onUploadEnd);
};
// Listener vor send() registrieren, sonst ggf. kein Upload-Event
xhr.upload.addEventListener('loadend', onUploadEnd);
xhr.addEventListener('loadend', () => { button.disabled = false; }, { once: true });
button.disabled = true;
const data = new FormData();
data.append('datei', input.files[0]);
xhr.open('POST', '/upload');
xhr.send(data);
};
button.addEventListener('click', sendFile);
XHR in ein Promise mit Aufräumlogik verpacken
const request = (url) => new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
let outcome = 'unbekannt';
xhr.onload = () => { outcome = 'load'; resolve(xhr.response); };
xhr.onerror = () => { outcome = 'error'; reject(new Error('Netzwerkfehler')); };
xhr.onabort = () => { outcome = 'abort'; reject(new Error('Abgebrochen')); };
xhr.ontimeout = () => { outcome = 'timeout'; reject(new Error('Timeout')); };
xhr.onloadend = () => console.log('loadend nach:', outcome);
xhr.open('GET', url);
xhr.send();
});
const main = async () => {
try {
const data = await request('/api/data');
console.log('Daten:', data);
} catch (err) {
console.error(err.message);
}
};
main();
// Wichtig · Fallstricke
- Kein Erfolgssignal:
loadendfeuert auch bei Fehlern. Erfolg immer überxhr.status(bzw. das vorherigeload-Ereignis) prüfen. Ein HTTP-Status 404 oder 500 löstloadaus, nichterror. - status 0: Bei Abbruch, Timeout, Netzwerk- oder CORS-Fehlern ist
xhr.statusimloadend-Handler0; Details zum CORS-Fehler sind aus Sicherheitsgründen nicht auslesbar. - Reihenfolge:
loadendkommt nachload/error/abort/timeout;readystatechangemitreadyState === 4wird davor ausgelöst. - Upload-Listener: Listener auf
xhr.uploadmüssen vorsend()registriert werden. Ist ein Upload-Listener registriert, wird bei Cross-Origin-Anfragen ein CORS-Preflight erzwungen. - Synchrone XHR (
open(..., false)) ist veraltet und löst im Hauptthread keine zuverlässigen Fortschrittsereignisse aus. - Kompatibilität: In allen modernen Browsern unterstützt (IE ab Version 10). Für neuen Code ist
fetch()mittry/finallymeist die bessere Wahl; XHR bleibt vor allem für Upload-Fortschritt relevant.