Chapter 10: XML HttpRequest
1. What is XMLHttpRequest really?
XMLHttpRequest is a built-in browser API that allows JavaScript code running in the browser to make HTTP requests to a server — without reloading the entire page.
It is the technology that made AJAX (Asynchronous JavaScript and XML) possible.
Important truth in 2025–2026:
- The name contains “XML” because originally it was mainly used to receive XML data
- Today almost nobody uses it for XML — most use JSON
- But the name never changed — so we still call it XMLHttpRequest
Think of it like this:
It’s the old, reliable school bus that still runs every day — even though newer, shinier electric buses (fetch + async/await) exist.
Many companies, old codebases, tutorials, and libraries still use XHR, so you must understand it.
2. Why was it invented? (Quick history – just the important part)
Before ~2005:
- Every time you clicked something → full page reload
- Very slow and bad user experience
Then Google Maps (2004–2005) showed the world:
“We can update only part of the page by talking to the server in the background!”
→ XMLHttpRequest became the standard way to do this.
Later → jQuery .ajax(), then fetch(), but XHR is still everywhere in legacy code.
3. Basic Structure – Step by Step
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
// 1. Create the object let xhr = new XMLHttpRequest(); // 2. Configure it (method, URL, async flag) xhr.open("GET", "https://api.example.com/data.json", true); // ↑ method ↑ url ↑ async (true = non-blocking) // 3. Set headers (optional but very common) xhr.setRequestHeader("Content-Type", "application/json"); xhr.setRequestHeader("Authorization", "Bearer your-token-here"); // 4. Define what happens when response arrives xhr.onload = function () { if (xhr.status >= 200 && xhr.status < 300) { // Success console.log("Success!", xhr.responseText); // Usually we parse JSON today let data = JSON.parse(xhr.responseText); console.log(data); } else { // Error console.log("Error:", xhr.status, xhr.statusText); } }; // 5. Handle network errors xhr.onerror = function () { console.log("Network error occurred"); }; // 6. Send the request xhr.send(); // for GET / HEAD // xhr.send(body); // for POST / PUT with data |
This is the classic pattern — you will see it in millions of codebases.
4. Real Practical Examples
Example 1 – Simple GET request (most common use)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
function loadUserProfile(userId) { let xhr = new XMLHttpRequest(); xhr.open("GET", `/api/users/${userId}`, true); xhr.onload = function () { if (xhr.status === 200) { let user = JSON.parse(xhr.responseText); document.getElementById("username").textContent = user.name; document.getElementById("email").textContent = user.email; } else { alert("Cannot load user: " + xhr.status); } }; xhr.onerror = function () { alert("Network error"); }; xhr.send(); } // Usage loadUserProfile(142); |
Example 2 – POST request with JSON data
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
function createNewTask(taskData) { let xhr = new XMLHttpRequest(); xhr.open("POST", "/api/tasks", true); // Very important header when sending JSON xhr.setRequestHeader("Content-Type", "application/json"); xhr.onload = function () { if (xhr.status === 201) { console.log("Task created!", JSON.parse(xhr.responseText)); } else { console.log("Failed:", xhr.status, xhr.responseText); } }; xhr.onerror = function () { console.log("Request failed"); }; // Convert object to JSON string let jsonData = JSON.stringify(taskData); xhr.send(jsonData); } // Usage createNewTask({ title: "Finish XML tutorial", priority: "high", dueDate: "2025-07-10" }); |
Example 3 – Showing loading state (very common UX pattern)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
function fetchProducts() { const loader = document.getElementById("loading"); const errorDiv = document.getElementById("error"); const productList = document.getElementById("products"); loader.style.display = "block"; errorDiv.style.display = "none"; let xhr = new XMLHttpRequest(); xhr.open("GET", "/api/products?category=electronics", true); xhr.onload = function () { loader.style.display = "none"; if (xhr.status === 200) { let products = JSON.parse(xhr.responseText); productList.innerHTML = products.map(p => `<li>${p.name} - ₹${p.price}</li>`).join(""); } else { errorDiv.textContent = `Error ${xhr.status}: ${xhr.statusText}`; errorDiv.style.display = "block"; } }; xhr.send(); } |
5. Important Properties & Events You Must Know
| Property / Event | What it contains / when it fires | Typical usage |
|---|---|---|
| xhr.readyState | 0 = unsent, 1 = opened, 2 = headers received, 3 = loading, 4 = done | Check if request is complete (usually wait for 4) |
| xhr.status | HTTP status code (200, 404, 500…) | Check success: 200–299 |
| xhr.statusText | Status message (“OK”, “Not Found”, …) | Show in error messages |
| xhr.responseText | Response as string | Most common when expecting JSON / text |
| xhr.responseXML | Parsed XML document (if server sent Content-Type: application/xml) | Used when really working with XML |
| xhr.response | Depends on responseType (can be JSON, Blob, ArrayBuffer…) | Modern usage |
| onload | Fires when request completes (status 0 or 200–599) | Main success/error handler |
| onerror | Fires on network failure (no internet, CORS block…) | Handle network-level errors |
| onprogress | Progress events (upload/download) | Show progress bars |
6. Common Patterns & Modern Additions
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// Modern way – set responseType = "json" xhr.responseType = "json"; xhr.onload = function () { if (xhr.status >= 200 && xhr.status < 300) { // No need to parse! console.log(xhr.response); // already an object } }; |
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
// Upload progress (file upload example) xhr.upload.onprogress = function (e) { if (e.lengthComputable) { let percent = (e.loaded / e.total) * 100; console.log(`Upload progress: ${percent.toFixed(1)}%`); } }; |
7. Very Common Mistakes Beginners Make
- Forgetting to call xhr.send()
- Sending JSON without Content-Type: application/json
- Not parsing responseText → treating string as object
- Not handling errors (status !== 200)
- Using synchronous mode (xhr.open(…, false)) → freezes browser!
- Not checking readyState === 4 when using onreadystatechange
- Ignoring CORS errors
Quick Summary – XMLHttpRequest Cheat Sheet
- new XMLHttpRequest() → create
- .open(method, url, async) → configure
- .setRequestHeader() → add headers
- .send(data?) → start request
- onload / onerror → handle response
- responseText / response → get data
- Most used today: JSON, POST, GET, progress
Even though fetch() is nicer, you will meet XHR in:
- Old company code
- WordPress plugins
- Legacy admin panels
- Some chart libraries
- File upload components
- Code you read in tutorials/books
So understanding XHR is still very useful in 2025–2026.
Would you like to continue with one of these next?
- Comparison: XMLHttpRequest vs fetch() (detailed side-by-side)
- Real file upload example with progress bar using XHR
- How XHR works with CORS and common errors
- onreadystatechange vs onload – when to use which
- How to timeout an XHR request
- Using XHR with XML data (real old-school example)
Tell me which direction feels most useful for you right now! 😊
