Start · Sprachen · JavaScript · Referenz · XMLHttpRequestEventTarget: loadend

XMLHttpRequestEventTarget: loadend

Ereignis

Wird ausgelöst, wenn eine XHR-Anfrage beendet ist – egal ob erfolgreich (nach <code>load</code>) oder nicht (nach <code>abort</code>, <code>error</code> oder <code>timeout</code>).

seit JavaScript Baseline: alle Browser (XMLHttpRequest Level 2) Kategorie: webapi

Signatur

xhr.addEventListener("loadend", handler) / xhr.upload.addEventListener("loadend", handler)

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

Typ
void
Beschreibung
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();
Status: 200 Fertig: 1532 Bytes empfangen

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();
Erfolg: 4821 Zeichen

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);
Upload beendet: 204800/204800 Bytes

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();
loadend nach: load Daten: {...}

// Wichtig · Fallstricke

  • Kein Erfolgssignal: loadend feuert auch bei Fehlern. Erfolg immer über xhr.status (bzw. das vorherige load-Ereignis) prüfen. Ein HTTP-Status 404 oder 500 löst load aus, nicht error.
  • status 0: Bei Abbruch, Timeout, Netzwerk- oder CORS-Fehlern ist xhr.status im loadend-Handler 0; Details zum CORS-Fehler sind aus Sicherheitsgründen nicht auslesbar.
  • Reihenfolge: loadend kommt nach load/error/abort/timeout; readystatechange mit readyState === 4 wird davor ausgelöst.
  • Upload-Listener: Listener auf xhr.upload müssen vor send() 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() mit try/finally meist die bessere Wahl; XHR bleibt vor allem für Upload-Fortschritt relevant.