Chapter 31: AJAX Examples
AJAX Examples, written as if I am your patient teacher sitting next to you.
We will go slowly and thoroughly, building understanding step by step. I will show you several progressively more realistic examples, explain every line, point out common mistakes, show what the network looks like, and explain why we write it this way in real projects (2025–2026 reality).
1. Very First Example – The absolute simplest AJAX GET
Goal: Click a button → get a message from the server → show it on the page (no reload)
|
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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>AJAX Example 1 – Simplest Possible</title> <style> body { font-family: Arial, sans-serif; margin: 40px; background: #f9fafb; } button { padding: 14px 32px; font-size: 18px; background: #2563eb; color: white; border: none; border-radius: 6px; cursor: pointer; } button:hover { background: #1d4ed8; } #result { margin-top: 30px; padding: 24px; background: white; border: 1px solid #d1d5db; border-radius: 8px; min-height: 80px; font-size: 17px; } </style> </head> <body> <h1>Example 1 – The Simplest AJAX GET</h1> <p>Click the button — only the box below changes (no page reload)</p> <button onclick="getMessage()">Click me to get message from server</button> <div id="result">Waiting for you to click...</div> <script> function getMessage() { // 1. Create the request object const xhr = new XMLHttpRequest(); // 2. Configure: method, URL, async (true = don't freeze browser) xhr.open("GET", "https://httpbin.org/anything/hello-world", true); // 3. What to do when response arrives xhr.onload = function () { // Check if request succeeded if (xhr.status >= 200 && xhr.status < 300) { // Show the response text document.getElementById("result").innerHTML = ` <strong>Server says:</strong><br> ${xhr.responseText} `; } else { document.getElementById("result").innerHTML = `Error ${xhr.status}: ${xhr.statusText}`; } }; // 4. Handle network-level errors (no internet, blocked, etc.) xhr.onerror = function () { document.getElementById("result").innerHTML = "Network error – cannot reach the server"; }; // 5. Send the request (no body for GET) xhr.send(); } </script> </body> </html> |
What you see in browser developer tools (F12 → Network tab):
- A request appears named hello-world
- Method: GET
- Status: 200 OK
- Response headers include Content-Type: application/json
- Response body contains the echoed “hello-world” wrapped in JSON
Important lessons from this first example:
- xhr.open() prepares the request
- xhr.send() actually sends it
- xhr.onload fires when complete (readyState 4)
- Always check xhr.status — 200–299 = success
- onerror catches network problems (offline, DNS error, blocked by CORS…)
2. Example 2 – Using modern fetch (what you should write today)
|
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 |
<button onclick="getMessageModern()">Modern fetch version</button> <script> async function getMessageModern() { const result = document.getElementById("result"); result.innerHTML = "Loading..."; try { const response = await fetch("https://httpbin.org/anything/modern-fetch"); if (!response.ok) { throw new Error(`Server error: {response.status}`); } const text = await response.text(); result.innerHTML = ` <strong>Modern fetch response:</strong><br> ${text} `; } catch (err) { result.innerHTML = `<span style="color:red">Error: ${err.message}</span>`; } } </script> |
Why fetch is better (2025–2026 standard):
- Promise-based → easy await
- Cleaner syntax
- response.ok is very convenient
- Built-in browser API — no need for libraries in most cases
3. Example 3 – Real-world style: Search with loading state & error handling
|
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 40 41 42 43 44 |
<input type="text" id="searchInput" placeholder="Type product name..." style="padding:12px; width:400px; font-size:16px;"> <div id="result" style="margin-top:20px; padding:20px; background:#fff; border:1px solid #ddd; min-height:100px;"></div> <script> const input = document.getElementById('searchInput'); const result = document.getElementById('result'); let timeoutId; input.addEventListener('input', function () { clearTimeout(timeoutId); const term = this.value.trim(); if (term.length < 2) { result.innerHTML = "Type at least 2 characters..."; return; } result.innerHTML = "<em>Searching...</em>"; timeoutId = setTimeout(async () => { try { const res = await fetch(`https://httpbin.org/anything?q=${encodeURIComponent(term)}`); if (!res.ok) throw new Error(`HTTP {res.status}`); const data = await res.json(); result.innerHTML = ` <strong>Search results for:</strong> "${term}"<br><br> <pre>${JSON.stringify(data.args, null, 2)}</pre> `; } catch (err) { result.innerHTML = `<span style="color:red">Error: ${err.message}</span>`; } }, 500); // 500 ms debounce }); </script> |
Key professional features here:
- Debounce – wait until user stops typing
- Loading state – shows “Searching…”
- Error handling – nice message if something fails
- encodeURIComponent – safe for special characters
4. Example 4 – POST with JSON (very common modern 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
<form id="loginForm"> <input type="text" id="username" placeholder="Username" required><br><br> <input type="password" id="password" placeholder="Password" required><br><br> <button type="submit">Login (AJAX)</button> </form> <div id="result"></div> <script> document.getElementById('loginForm').addEventListener('submit', async function(e) { e.preventDefault(); const result = document.getElementById('result'); result.innerHTML = "Logging in..."; const data = { username: document.getElementById('username').value, password: document.getElementById('password').value }; try { const response = await fetch('https://httpbin.org/post', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!response.ok) { throw new Error(`Login failed: {response.status}`); } const json = await response.json(); result.innerHTML = ` <strong style="color:green">Success!</strong><br><br> Server echoed back your data:<br> <pre>${JSON.stringify(json.json, null, 2)}</pre> `; } catch (err) { result.innerHTML = `<strong style="color:red">Error:</strong> ${err.message}`; } }); </script> |
Summary – What we learned across these examples
| Pattern | When to use it | Key technique |
|---|---|---|
| Simple GET | Load static data, configs, messages | fetch(url) |
| Search / autocomplete | Live filtering as user types | input event + debounce |
| Form POST (JSON) | Login, registration, comments, orders | fetch(…, {method:’POST’, body}) |
| Loading + error states | All real applications | try/catch, show loading message |
Final 2025–2026 advice
- Use fetch + async/await for almost everything new
- Always handle errors — users hate silent failures
- Add loading indicators — even 300 ms feels long without feedback
- Debounce search inputs (300–600 ms) — saves server load
- Validate on client before sending (empty fields, email format…)
- Never trust client data — validate again on server
Would you like to go deeper into one of these realistic directions?
- Add loading spinner animation
- Show “No results found” nicely
- Click outside to close suggestions
- Product details modal after selection
- Add to cart (session or real database)
- Pagination (“Show more” button)
Just tell me which one you want next! 😊
