XMLHttpRequest is a built-in browser object that allows to make HTTP requests in JavaScript Can operate on any data, not only in XML Can upload/download files, track progress and much more Fetch somewhat deprecates XMLHttpRequest Used for support existing scripts with XMLHttpRequest support old browsers need something that fetch can’t do yet, e.g. to track upload progress Create const xhr = new XMLHttpRequest() // create xhr.open(method, URL, [async, user, password]) // initialize xhr.open('GET', 'https://google.com') method HTTP-method. Usually "GET" or "POST" URL the URL to request, a string, can be URL object async if explicitly set to false, then the request is synchronous, we’ll cover that a bit later user , password login and password for basic HTTP auth (if required) const url = new URL('https://google.com/search') url.searchParams.set('q', 'test me!') // https://google.com/search?q=test+me%21 xhr.open('GET', url) Start connection xhr.send([body]) body parameter contains the request body Some request methods like GET do not have a body Events for response Listen to xhr events for response, events are the most widely used 'load' request is complete (even if HTTP status is like 400 or 500), and the response is fully downloaded 'error' request couldn’t be made, e.g. network down or invalid URL. 'progress' triggers periodically while the response is being downloaded, reports how much has been downloaded. 'readystatechange' old event, do not use it Properties xhr.status HTTP status code (a number): 200, 404, 403 and so on, can be 0 in case of a non-HTTP failure xhr.statusText HTTP status message: usually 'OK' for 200, 'Not Found' for 404, 'Forbidden' for 403 and so on xhr.response server response body xhr.responseText same, used in old scripts xhr.responseXML used in old scripts xhr.timeout = 10000 timeout in ms, if the request does not succeed in 10s, it gets canceled and timeout event triggers xhr.responseType = '' property to set the response format, get as string - (default) xhr.responseType = 'text' get as string xhr.responseType = 'arraybuffer' get as ArrayBuffer (for binary data) xhr.responseType = 'blob' get as Blob (for binary data) xhr.responseType = 'document' get as XML document (can use XPath and other XML methods) or HTML document (based on the MIME type of the received data) xhr.responseType = 'json' get as JSON (parsed automatically) xhr.readyState XMLHttpRequest changes between states as it progresses xhr.setRequestHeader(name, value) sets the request header with the given name and value xhr.getResponseHeader(name) gets the response header with the given name (except Set-Cookie and Set-Cookie2) xhr.getAllResponseHeaders() returns all response headers, except Set-Cookie and Set-Cookie2 xhr.withCredentials = true send cookies and HTTP-authorization to another origin Examples const xhr = new XMLHttpRequest() // 1. Create a new XMLHttpRequest object xhr.open('GET', '/article/xmlhttprequest/example/load') // 2. Configure it: GET-request for the URL /article/.../load xhr.send() // 3. Send the request over the network xhr.onload = function() { // 4. This will be called after the response is received if (xhr.status != 200) { // analyze HTTP status of the response alert(`Error ${xhr.status}: ${xhr.statusText}`); return; // e.g. 404: Not Found } alert(`Done, got ${xhr.response.length} bytes`) // response is the server response // get the response from xhr.response } xhr.onprogress = function(event) { if (event.lengthComputable) { alert(`Received ${event.loaded} of ${event.total} bytes`) } else { alert(`Received ${event.loaded} bytes`) // no Content-Length } } xhr.onerror = function() { alert("Request failed") } const xhr = new XMLHttpRequest() xhr.open('GET', '/article/xmlhttprequest/example/json') xhr.responseType = 'json' xhr.send() xhr.onload = function() { let responseObj = xhr.response // {"message": "Hello, world!"} alert(responseObj.message) // Hello, world! } xhr.readyState xhr.readyState XMLHttpRequest changes between states as it progresses xhr.readyState = 0 initial state xhr.readyState = 1 open called xhr.readyState = 2 response headers received xhr.readyState = 3 response is loading (a data packet is received) xhr.readyState = 4 request complete We can track state using readystatechange event It is an old approach, do not use it xhr.onreadystatechange = function() { if (xhr.readyState == 3) { // loading } if (xhr.readyState == 4) { // request finished } } const xhr = new XMLHttpRequest(); xhr.onreadystatechange = () => { switch (xhr.readyState) { case 0: // UNSENT, Client has been created. open() not called yet. console.log('0 UNSENT', xhr.statusText); break; case 1: // OPENED, open() has been called. console.log('1 OPENED', xhr.statusText); break; case 2: // HEADERS_RECEIVED, send() has been called, and headers and status are available. console.log('2 HEADERS_RECEIVED', xhr.statusText); break; case 3: // LOADING, Downloading; responseText holds partial data. console.log('3 LOADING', xhr.statusText); console.log('interactive... ' + xhr.responseText.length + ' bytes.'); break; case 4: // DONE, The operation is complete. console.log('4 DONE', xhr.statusText); const header = xhr.getResponseHeader('Content-Type'); const headers = xhr.getAllResponseHeaders(); if (xhr.status == 200 || xhr.status == 304) { var data = xhr.responseText; console.log('COMPLETE!'); console.dir(data); } else { console.log('Failed. HttpStatus: ' + xhr.statusText); } break; } }; xhr.withCredentials = true; xhr.responseType = 'json'; xhr.setRequestHeader('Content-Type', 'application/json'); xhr.open('GET', '/server'); xhr.send(null); Synchronous requests 3rd param 'async' is set to 'false', the request is made synchronously JavaScript pauses at send() and resumes when the response is received const xhr = new XMLHttpRequest() xhr.open('GET', '/article/xmlhttprequest/hello.txt', false) try { xhr.send() if (xhr.status != 200) { alert(`Error ${xhr.status}: ${xhr.statusText}`) } else { alert(xhr.response) } } catch(err) { // instead of onerror alert("Request failed") } HTTP-headers setRequestHeader xhr.setRequestHeader(name, value) Sets the request header with the given name and value. Several headers are managed exclusively by the browser, e.g. 'Referer' and 'Host' Once the header is set, it’s set, can not overwrite it after xhr.setRequestHeader('Content-Type', 'application/json') getResponseHeader xhr.getResponseHeader(name) Gets the response header with the given name (except Set-Cookie and Set-Cookie2). xhr.getResponseHeader('Content-Type') getAllResponseHeaders Returns all response headers, except Set-Cookie and Set-Cookie2 Headers are returned as a single line The line break between headers is always "\r\n" we can easily split it into individual headers The separator between the name and the value is always a colon followed by a space ": " let headers = xhr .getAllResponseHeaders() .split(' ') .reduce((result, current) => { let [name, value] = current.split(': ') result[name] = value return result }, {}) // headers['Content-Type'] = 'image/png' POST with FormData To make a POST request, we can use the built-in FormData object. <form name="person"> <input name="name" value="John"> <input name="surname" value="Smith"> </form> const formData = new FormData(document.forms.person) // pre-fill FormData from the form formData.append("middle", "Lee") // add one more field const xhr = new XMLHttpRequest() xhr.open("POST", "/article/xmlhttprequest/post/user") xhr.send(formData) xhr.onload = () => alert(xhr.response) // The form is sent with multipart/form-data encoding // if we like JSON, then JSON.stringify and send as a string const xhr = new XMLHttpRequest() const json = JSON.stringify({ name: "John", surname: "Smith" }) xhr.open("POST", '/submit') xhr.setRequestHeader('Content-type', 'application/json charset=utf-8') xhr.send(json) // send(body) method can send almost any body, including Blob and BufferSource objects Upload progress xhr.upload generates events and triggers them on uploading xhr.upload = 'loadstart' upload started xhr.upload = 'progress' triggers periodically during the upload xhr.upload = 'abort' upload aborted xhr.upload = 'error' non-HTTP error xhr.upload = 'load' upload finished successfully xhr.upload = 'timeout' upload timed out (if timeout property is set) xhr.upload = 'loadend' upload finished with either success or error xhr.upload.onprogress = (e) => alert(`Uploaded ${e.loaded} of ${e.total} bytes`) xhr.upload.onload = () => alert(`Upload finished successfully.`) xhr.upload.onerror = () => alert(`Error during the upload: ${xhr.status}`) <input type="file" onchange="upload(this.files[0])"> function upload(file) { let xhr = new XMLHttpRequest() // track upload progress xhr.upload.onprogress = function(e) { console.log(`Uploaded ${e.loaded} of ${e.total}`) } // track completion: both successful or not xhr.onloadend = function() { if (xhr.status == 200) { console.log("success") } else { console.log("error " + this.status) } } xhr.open("POST", "/article/xmlhttprequest/post/upload") xhr.send(file) } Cross-origin requests XMLHttpRequest can make cross-origin requests, using the same CORS policy as fetch Just like fetch, it doesn’t send cookies and HTTP-authorization to another origin by default To enable them, set xhr.withCredentials to true const xhr = new XMLHttpRequest() xhr.withCredentials = true xhr.open('POST', 'http://anywhere.com/request')