Signatur
Beschreibung
send() ist der letzte Schritt einer XMLHttpRequest-Anfrage: Nachdem mit open() Methode und URL festgelegt und gegebenenfalls mit setRequestHeader() Header gesetzt wurden, schickt send() die Anfrage tatsächlich ab. Bei asynchronen Anfragen (Standard) kehrt die Methode sofort zurück. Das Ergebnis erhältst du über Events wie load, error, progress oder readystatechange.
Über den optionalen Parameter body übergibst du Daten, etwa bei POST oder PUT. Erlaubt sind unter anderem Zeichenketten (z. B. JSON), FormData, URLSearchParams, Blob/File, ArrayBuffer, typisierte Arrays und Document. Je nach Typ setzt der Browser automatisch einen passenden Content-Type, z. B. multipart/form-data mit Boundary bei FormData.
Bei GET- und HEAD-Anfragen wird ein übergebener Body ignoriert und als null behandelt. In neuem Code ist meist fetch() die bessere Wahl. XMLHttpRequest ist aber weiterhin sinnvoll, wenn du den Upload-Fortschritt über xhr.upload.onprogress brauchst. Das kann fetch() bis heute nicht einfach.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $body | string | Document | Blob | ArrayBuffer | ArrayBufferView | FormData | URLSearchParams | ReadableStream | null | null | Der Anfragekörper. Wird bei GET und HEAD ignoriert. Ohne Angabe oder bei null wird kein Body gesendet. |
Rückgabewert
undefined zurück. Die Antwort steht nach Abschluss in xhr.response, xhr.responseText und xhr.status bereit.Beispiele
Einfache GET-Anfrage mit JSON-Antwort
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(); // kein Body bei GET
JSON per POST senden
const neuerEintrag = { title: 'Einkaufen', completed: false, userId: 1 };
const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://jsonplaceholder.typicode.com/todos');
xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
xhr.responseType = 'json';
xhr.onload = () => {
console.log(xhr.status, xhr.response);
};
xhr.send(JSON.stringify(neuerEintrag));
Datei-Upload mit Fortschrittsanzeige (FormData)
const input = document.querySelector('#datei');
const balken = document.querySelector('progress');
input.addEventListener('change', () => {
const formData = new FormData();
formData.append('datei', input.files[0]);
const xhr = new XMLHttpRequest();
xhr.open('POST', '/api/upload');
// Upload-Fortschritt – mit fetch() nicht direkt möglich
xhr.upload.onprogress = (e) => {
if (e.lengthComputable) {
balken.value = Math.round((e.loaded / e.total) * 100);
}
};
xhr.onload = () => console.log('Upload fertig:', xhr.status);
// Content-Type NICHT manuell setzen – der Browser ergänzt die Boundary
xhr.send(formData);
});
send() in ein Promise verpacken
const request = (method, url, body = null) =>
new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.responseType = 'json';
xhr.timeout = 5000;
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);
});
const main = async () => {
const user = await request('GET', 'https://jsonplaceholder.typicode.com/users/1');
console.log(user.name);
};
main();
// Wichtig · Fallstricke
- Reihenfolge:
send()darf erst nachopen()aufgerufen werden. Sonst wirft es eineInvalidStateError-DOMException. Ein zweitersend()-Aufruf auf dieselbe Anfrage ohne erneutesopen()führt ebenfalls zu diesem Fehler. - HTTP-Fehler sind keine Netzwerkfehler: Auch bei 404 oder 500 wird
loadausgelöst. Prüfe deshalb immerxhr.status.errortritt nur bei Netzwerk- oder CORS-Problemen auf. - Synchrone Anfragen (
open(method, url, false)) blockieren den Haupt-Thread. Im Hauptkontext gelten sie als veraltet und erzeugen Konsolenwarnungen.timeoutundresponseTypesind dort nicht nutzbar. - FormData: Setze keinen eigenen
Content-Type-Header, sonst fehlt die Multipart-Boundary und der Server kann den Body nicht auswerten. - CORS: Anfragen an fremde Origins unterliegen der Same-Origin-Policy. Eigene Header oder ein
Content-Type: application/jsonlösen eine Preflight-Anfrage (OPTIONS) aus. Cookies werden nur mitxhr.withCredentials = truemitgesendet. - Kompatibilität: Wird von allen Browsern unterstützt.
ReadableStreamals Body wird praktisch nicht unterstützt. In Service Workern gibt es keinXMLHttpRequest, dort nutzt dufetch().