Start · Sprachen · JavaScript · Referenz · XMLHttpRequest

XMLHttpRequest

Schnittstelle

Mit <code>XMLHttpRequest</code> sendet man HTTP-Anfragen an einen Server und empfängt die Antworten, ohne die Seite neu zu laden. Die API ist ereignisbasiert.

seit JavaScript Baseline: alle Browser (Level 2 / XHR Living Stand Kategorie: webapi

Signatur

interface XMLHttpRequest extends XMLHttpRequestEventTarget

Beschreibung

XMLHttpRequest (XHR) ist die klassische Browser-Schnittstelle für HTTP-Anfragen aus JavaScript und die technische Grundlage von „AJAX“. Trotz des Namens verarbeitet XHR beliebige Daten: Text, JSON, HTML-Dokumente, Blob und ArrayBuffer. Außerdem funktioniert es nicht nur mit HTTP(S).

Der Ablauf besteht aus festen Schritten:

  • Instanz mit new XMLHttpRequest() erzeugen.
  • open(method, url) aufrufen.
  • Optional Header mit setRequestHeader() setzen und responseType, timeout oder withCredentials festlegen.
  • Die Anfrage mit send(body) abschicken.

Das Ergebnis kommt über Ereignisse wie load, error, timeout, abort und progress oder über readystatechange zusammen mit readyState. Wichtige Eigenschaften:

  • readyState: Zustand von 0 bis 4.
  • status und statusText: HTTP-Status der Antwort.
  • response, responseText und responseXML: die empfangenen Daten.
  • upload: Fortschrittsereignisse beim Hochladen.

In neuem Code nimmt man meist die Promise-basierte Fetch-API. XHR ist aber weiterhin nützlich, wenn man den Upload-Fortschritt anzeigen will (xhr.upload.onprogress). Das kann fetch() bis heute nicht einfach. XHR begegnet einem außerdem in älteren Codebasen und Bibliotheken.

Parameter

Name Typ Default Beschreibung
$method Pflicht string Parameter von open(): HTTP-Methode, z. B. "GET", "POST", "PUT" oder "DELETE".
$url Pflicht string | URL Parameter von open(): Ziel-URL der Anfrage, absolut oder relativ zum Dokument.
$async boolean true Parameter von open(): Bei false läuft die Anfrage synchron. Davon wird im Hauptthread abgeraten.
$user / password string | null null Parameter von open(): Optionale Zugangsdaten für die HTTP-Authentifizierung.
$body string | FormData | Blob | ArrayBuffer | URLSearchParams | Document | null null Parameter von send(): Inhalt der Anfrage. Bei GET und HEAD wird er ignoriert.

Beispiele

JSON per GET laden

const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://jsonplaceholder.typicode.com/todos/1');
xhr.responseType = 'json';

xhr.addEventListener('load', () => {
  if (xhr.status >= 200 && xhr.status < 300) {
    console.log('Titel:', xhr.response.title);
  } else {
    console.error('HTTP-Fehler:', xhr.status);
  }
});
xhr.addEventListener('error', () => console.error('Netzwerkfehler'));

xhr.send();
Titel: delectus aut autem

XHR als Promise mit async/await nutzen

const request = (method, url, body = null) =>
  new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open(method, url);
    xhr.responseType = 'json';
    xhr.timeout = 5000; // 5 Sekunden
    xhr.setRequestHeader('Content-Type', 'application/json');
    xhr.onload = () =>
      xhr.status < 400 ? resolve(xhr.response) : reject(new Error(`HTTP ${xhr.status}`));
    xhr.onerror = () => reject(new Error('Netzwerkfehler'));
    xhr.ontimeout = () => reject(new Error('Zeitüberschreitung'));
    xhr.send(body ? JSON.stringify(body) : null);
  });

const main = async () => {
  const post = await request('POST', 'https://jsonplaceholder.typicode.com/posts', {
    title: 'Hallo',
    body: 'Welt',
    userId: 1
  });
  console.log('Neue ID:', post.id);
};
main().catch(console.error);
Neue ID: 101

Datei-Upload mit Fortschrittsanzeige

// HTML: <input type="file" id="datei"> <progress id="balken" max="100" value="0"></progress>
const input = document.querySelector('#datei');
const balken = document.querySelector('#balken');

input.addEventListener('change', () => {
  const formData = new FormData();
  formData.append('upload', input.files[0]);

  const xhr = new XMLHttpRequest();
  xhr.upload.addEventListener('progress', (e) => {
    if (e.lengthComputable) {
      balken.value = Math.round((e.loaded / e.total) * 100);
    }
  });
  xhr.addEventListener('load', () => console.log('Upload fertig:', xhr.status));
  xhr.open('POST', '/api/upload');
  xhr.send(formData); // Content-Type (multipart) setzt der Browser selbst
});
Upload fertig: 200

Anfrage abbrechen mit abort()

const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/grosse-datei');
xhr.onabort = () => console.log('Anfrage abgebrochen');
xhr.send();

// Nach 1 Sekunde abbrechen, z. B. wenn der Nutzer weiternavigiert
setTimeout(() => xhr.abort(), 1000);
Anfrage abgebrochen

// Wichtig · Fallstricke

  • Das Ereignis error meldet keine HTTP-Fehler: Auch bei 404 oder 500 wird load ausgelöst. Deshalb muss man status immer selbst prüfen. error tritt nur bei Netzwerkfehlern oder CORS-Blockaden auf. In diesen Fällen ist status gleich 0.
  • Reihenfolge der Aufrufe: setRequestHeader() funktioniert nur zwischen open() und send(). Andernfalls wird ein InvalidStateError geworfen. responseType setzt man vor send().
  • Synchrone Anfragen (async = false) blockieren den Hauptthread und sind dort veraltet. Außerhalb von Workern darf responseType in diesem Fall nicht gesetzt werden.
  • CORS: Bei Anfragen an einen anderen Ursprung muss der Server passende Access-Control-Allow-*-Header senden. Cookies werden nur mit withCredentials = true mitgeschickt, und dann darf der Server nicht mit Access-Control-Allow-Origin: * antworten.
  • Sicherheit: Serverantworten nie ungeprüft per innerHTML einfügen, sonst droht XSS. Besser textContent verwenden oder die Inhalte bereinigen. Die Verbindungsziele lassen sich per CSP mit connect-src einschränken.
  • Verfügbarkeit: XHR gibt es in allen Browsern und in Web Workern, aber nicht in Service Workern und nicht nativ in Node.js. Dort verwendet man fetch().
  • responseXML ist nur bei XML- oder HTML-Antworten gefüllt. Bei HTML muss dafür responseType = 'document' gesetzt sein.