XMLHttpRequest (XHR)
By Flavio Copes
Learn how XMLHttpRequest works, why Fetch is the default for new code, and when XHR is still useful for upload progress.
- Introduction
- An example XHR request
- Additional open() parameters
- onreadystatechange
- Aborting an XHR request
- Comparison with jQuery
- Comparison with Fetch
- Cross Domain Requests
- Uploading files using XHR
Introduction
XMLHttpRequest, usually called XHR, lets JavaScript make HTTP requests without reloading the page.
XHR is a legacy API, but it remains widely supported. The Fetch API is the better default for new code. XHR remains useful when you need upload progress events or maintain older code that already uses it.
XHR was invented at Microsoft in the 1990s. Apps like Gmail and Google Maps later showed what pages could do without full reloads.
Libraries like jQuery became popular partly because they hid early browser differences behind a simpler API.
An example XHR request
The following code creates an XHR object and makes an asynchronous GET request:
const xhr = new XMLHttpRequest()
xhr.addEventListener('load', () => {
if (xhr.status >= 200 && xhr.status < 300) {
console.log(xhr.responseText)
} else {
console.error(`HTTP error: ${xhr.status}`)
}
})
xhr.addEventListener('error', () => {
console.error('Network error')
})
xhr.open('GET', '/file.txt')
xhr.send()
Additional open() parameters
In the example above, we passed the method and URL to open().
XHR supports other HTTP methods, including POST, PUT, PATCH, and DELETE.
The third parameter controls whether the request is asynchronous:
xhr.open('GET', '/file.txt', true)
It defaults to true. Do not set it to false in page code. Synchronous XHR is deprecated because it blocks the main thread and can make the page freeze.
onreadystatechange
Older XHR code often uses readystatechange. The event fires several times as the request moves through its states.
The states are
- 0 (UNSENT):
open()has not been called - 1 (OPENED):
open()has been called - 2 (HEADERS_RECEIVED): the HTTP headers have been received
- 3 (LOADING): the response begins to download
- 4 (DONE): the request has completed
For new XHR code, load, error, abort, and timeout events are usually clearer.
Aborting an XHR request
An XHR request can be aborted by calling the abort() method on the xhr object.
Comparison with jQuery
With jQuery these lines can be translated to:
$.get('https://yoursite.com', (data) => {
console.log(data)
}).fail((err) => {
console.error(err)
})
Comparison with Fetch
With the Fetch API this is the equivalent code:
fetch('/file.txt')
.then((response) => {
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`)
}
return response.text()
})
.then((text) => console.log(text))
.catch((error) => console.error(error))
Cross Domain Requests
XHR follows the same-origin policy. To read a response from another origin, that server must allow your origin using CORS (Cross-Origin Resource Sharing).
Uploading files using XHR
XHR can report upload progress through its upload object. This is one reason it is still used:
xhr.upload.addEventListener('progress', (event) => {
if (event.lengthComputable) {
const percent = Math.round((event.loaded / event.total) * 100)
console.log(`${percent}% uploaded`)
}
})
See the MDN XHR progress-event guide and my tutorial on how to upload files using XHR.
Related posts about platform: