Start · Sprachen · JavaScript · Referenz · XMLHttpRequestEventTarget

XMLHttpRequestEventTarget

Schnittstelle

Abstraktes Interface, das die gemeinsamen Fortschritts-Ereignis-Handler (<code>onload</code>, <code>onprogress</code>, <code>onerror</code> …) für <code>XMLHttpRequest</code> und <code>XMLHttpRequestUpload</code> bereitstellt.

seit JavaScript Baseline: alle Browser Kategorie: webapi

Signatur

interface XMLHttpRequestEventTarget extends EventTarget

Beschreibung

XMLHttpRequestEventTarget ist ein abstraktes Interface: Man erzeugt es nie direkt. Es erbt von EventTarget und legt die Ereignis-Handler fest, die sowohl XMLHttpRequest (Download bzw. die gesamte Anfrage) als auch XMLHttpRequestUpload (erreichbar über xhr.upload) teilen. So lässt sich der Fortschritt beim Hoch- und Herunterladen mit demselben Satz Handler verfolgen.

Folgende Handler-Eigenschaften sind definiert. Jeder bekommt ein ProgressEvent übergeben:

  • onloadstart – die Übertragung beginnt.
  • onprogress – wird während der Übertragung regelmäßig ausgelöst (loaded, total, lengthComputable).
  • onabort – die Anfrage wurde mit abort() abgebrochen.
  • onerror – ein Netzwerkfehler ist aufgetreten (z. B. bei CORS-Ablehnung oder fehlender Verbindung).
  • onload – die Übertragung ist erfolgreich abgeschlossen (auch bei HTTP-Status 404 oder 500!).
  • ontimeout – das in xhr.timeout gesetzte Zeitlimit wurde überschritten.
  • onloadend – die Übertragung ist beendet, egal ob erfolgreich oder nicht (nach load, error, abort oder timeout).

Statt der on…-Eigenschaften kann man gleichwertig addEventListener("progress", handler) usw. verwenden – das erlaubt mehrere Listener und das gezielte Entfernen mit removeEventListener. Typischer Einsatzzweck ist heute vor allem die Upload-Fortschrittsanzeige, weil fetch() dafür keine einfache Entsprechung bietet.

Rückgabewert

Typ
XMLHttpRequestEventTarget
Beschreibung
Kein eigener Rückgabewert – das Interface wird über Instanzen von XMLHttpRequest bzw. xhr.upload (XMLHttpRequestUpload) genutzt.

Beispiele

Instanz erhalten und Vererbung prüfen

const xhr = new XMLHttpRequest();

// Beide Objekte implementieren XMLHttpRequestEventTarget
console.log(xhr instanceof XMLHttpRequestEventTarget);        // true
console.log(xhr.upload instanceof XMLHttpRequestEventTarget); // true
console.log(xhr instanceof EventTarget);                      // true

// Direkte Instanziierung ist nicht erlaubt
try {
  new XMLHttpRequestEventTarget();
} catch (err) {
  console.log(err.name);
}
true true true TypeError

Download mit Fortschritt, Fehler- und Timeout-Behandlung

const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/berichte.json');
xhr.responseType = 'json';
xhr.timeout = 10000;

xhr.onloadstart = () => console.log('Download startet');
xhr.onprogress = (e) => {
  if (e.lengthComputable) {
    console.log(`Fortschritt: ${Math.round((e.loaded / e.total) * 100)} %`);
  }
};
xhr.onload = () => {
  // load feuert auch bei 404/500 – Status prüfen!
  if (xhr.status >= 200 && xhr.status < 300) {
    console.log('Daten:', xhr.response);
  } else {
    console.warn('HTTP-Fehler', xhr.status);
  }
};
xhr.onerror = () => console.error('Netzwerkfehler');
xhr.ontimeout = () => console.error('Zeitlimit überschritten');
xhr.onloadend = () => console.log('Anfrage beendet');

xhr.send();
Download startet Fortschritt: 42 % Fortschritt: 100 % Daten: {...} Anfrage beendet

Upload-Fortschritt mit addEventListener / removeEventListener

const input = document.querySelector('#datei');
const balken = document.querySelector('progress');

const hochladen = (datei) => {
  const xhr = new XMLHttpRequest();
  const formData = new FormData();
  formData.append('datei', datei);

  const onProgress = (e) => {
    if (e.lengthComputable) {
      balken.max = e.total;
      balken.value = e.loaded;
    }
  };

  // Listener am Upload-Objekt (XMLHttpRequestUpload) VOR send() registrieren
  xhr.upload.addEventListener('progress', onProgress);
  xhr.upload.addEventListener('loadend', () => {
    xhr.upload.removeEventListener('progress', onProgress);
    console.log('Upload abgeschlossen');
  }, { once: true });

  xhr.open('POST', '/upload');
  xhr.send(formData);
};

input.addEventListener('change', () => {
  if (input.files.length) hochladen(input.files[0]);
});
Upload abgeschlossen

Promise-Wrapper mit Abbruch

const ladeText = (url, signal) => new Promise((resolve, reject) => {
  const xhr = new XMLHttpRequest();
  xhr.open('GET', url);
  xhr.addEventListener('load', () => resolve(xhr.responseText));
  xhr.addEventListener('error', () => reject(new Error('Netzwerkfehler')));
  xhr.addEventListener('abort', () => reject(new DOMException('Abgebrochen', 'AbortError')));
  signal?.addEventListener('abort', () => xhr.abort());
  xhr.send();
});

const controller = new AbortController();
ladeText('/langsam.txt', controller.signal).catch((e) => console.log(e.name));
controller.abort();
AbortError

// Wichtig · Fallstricke

  • load ≠ Erfolg: onload wird auch bei HTTP-Fehlercodes (404, 500) ausgelöst. onerror feuert nur bei Netzwerk- oder CORS-Fehlern. Immer xhr.status prüfen.
  • Upload-Listener vor send() registrieren: Listener auf xhr.upload, die erst nach send() angehängt werden, erfassen möglicherweise keine Ereignisse. Außerdem löst das Registrieren von Upload-Listenern bei Cross-Origin-Anfragen einen CORS-Preflight (OPTIONS) aus.
  • lengthComputable: Ist false, wenn der Server keinen Content-Length-Header sendet (oder die Antwort komprimiert/chunked ist). Dann ist total gleich 0.
  • Reihenfolge: loadstart → progress* → genau eines von load/error/abort/timeout → loadend. Aufräumarbeiten gehören deshalb nach loadend.
  • readystatechange gehört nicht zu diesem Interface, sondern nur zu XMLHttpRequest.
  • Kompatibilität: in allen modernen Browsern verfügbar, auch in Web Workern. In Service Workern gibt es kein XMLHttpRequest – dort fetch() verwenden. Für neuen Code ohne Upload-Fortschritt ist fetch() in der Regel die bessere Wahl.