Learning Objectives
By the end of this session, students will be able to:
- Understand AJAX and asynchronous communication
- Use XMLHttpRequest and Fetch API for HTTP requests
- Parse and generate XML data
- Work with JSON data format
- Handle API responses and errors
- Build dynamic web applications with AJAX
Introduction
AJAX (Asynchronous JavaScript and XML) allows web pages to update asynchronously by exchanging data with a server behind the scenes. This means parts of a web page can be updated without reloading the entire page.
Key Insight
AJAX revolutionized web development by enabling smooth, desktop-like user experiences in web applications. Modern frameworks like React, Vue, and Angular all rely heavily on AJAX principles.
Benefits of AJAX
- Better UX: No full page reloads needed
- Faster: Only exchange necessary data
- Reduced load: Less bandwidth usage
- Real-time: Update data without user action
- Responsive: Background data loading
AJAX Basics
AJAX is not a programming language but a technique that combines several technologies:
AJAX Components:
1. HTML/CSS - Structure and presentation
2. DOM - Dynamic content updates
3. XML/JSON - Data exchange formats
4. XMLHttpRequest or Fetch API - Async communication
5. JavaScript - Tie everything together
How AJAX Works
- An event occurs (button click, page load, timer)
- JavaScript creates an XMLHttpRequest/Fetch object
- Request is sent to web server
- Server processes the request
- Server sends response back
- JavaScript processes the response
- DOM is updated with new content
XMLHttpRequest API
The traditional way to make AJAX requests in JavaScript.
Basic GET Request
// Create XMLHttpRequest object
const xhr = new XMLHttpRequest();
// Configure request
xhr.open('GET', 'https://api.example.com/data', true);
// Set up callback function
xhr.onload = function() {
if (xhr.status === 200) {
console.log('Success:', xhr.responseText);
const data = JSON.parse(xhr.responseText);
// Use the data
} else {
console.error('Error:', xhr.status);
}
};
xhr.onerror = function() {
console.error('Request failed');
};
// Send request
xhr.send();
POST Request with Data
const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://api.example.com/users', true);
// Set headers
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = function() {
if (xhr.status === 201) {
console.log('Created:', xhr.responseText);
}
};
// Prepare data
const data = {
name: 'John Doe',
email: 'john@example.com'
};
// Send request with JSON data
xhr.send(JSON.stringify(data));
Tracking Progress
xhr.upload.addEventListener('progress', function(e) {
if (e.lengthComputable) {
const percentComplete = (e.loaded / e.total) * 100;
console.log('Upload:', percentComplete + '%');
}
});
xhr.addEventListener('progress', function(e) {
if (e.lengthComputable) {
const percentComplete = (e.loaded / e.total) * 100;
console.log('Download:', percentComplete + '%');
}
});
Fetch API
Modern, promise-based API for making HTTP requests. Cleaner and more powerful than XMLHttpRequest.
Basic GET Request
// Simple fetch
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
// With async/await
async function getData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Error:', error);
}
}
POST Request
async function createUser(userData) {
try {
const response = await fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(userData)
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log('Success:', data);
return data;
} catch (error) {
console.error('Error:', error);
}
}
// Usage
createUser({
name: 'John Doe',
email: 'john@example.com'
});
All HTTP Methods
// GET
fetch('/api/users')
.then(r => r.json())
.then(console.log);
// POST
fetch('/api/users', {
method: 'POST',
body: JSON.stringify({ name: 'John' }),
headers: { 'Content-Type': 'application/json' }
});
// PUT (update entire resource)
fetch('/api/users/1', {
method: 'PUT',
body: JSON.stringify({ name: 'John Doe', email: 'john@example.com' }),
headers: { 'Content-Type': 'application/json' }
});
// PATCH (partial update)
fetch('/api/users/1', {
method: 'PATCH',
body: JSON.stringify({ name: 'Jane Doe' }),
headers: { 'Content-Type': 'application/json' }
});
// DELETE
fetch('/api/users/1', {
method: 'DELETE'
});
Advanced Fetch Options
fetch('/api/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token123'
},
body: JSON.stringify(data),
mode: 'cors', // cors, no-cors, same-origin
credentials: 'include', // include, same-origin, omit
cache: 'no-cache', // default, no-cache, reload, force-cache
redirect: 'follow', // follow, error, manual
referrerPolicy: 'no-referrer'
});
Working with XML
XML (eXtensible Markup Language) is a markup language for storing and transporting data.
XML Structure Example
<?xml version="1.0" encoding="UTF-8"?>
<users>
<user id="1">
<name>John Doe</name>
<email>john@example.com</email>
<age>30</age>
</user>
<user id="2">
<name>Jane Smith</name>
<email>jane@example.com</email>
<age>28</age>
</user>
</users>
Parsing XML in JavaScript
// Fetch and parse XML
fetch('data.xml')
.then(response => response.text())
.then(xmlString => {
// Parse XML string
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlString, 'text/xml');
// Extract data
const users = xmlDoc.querySelectorAll('user');
users.forEach(user => {
const id = user.getAttribute('id');
const name = user.querySelector('name').textContent;
const email = user.querySelector('email').textContent;
const age = user.querySelector('age').textContent;
console.log({ id, name, email, age });
});
});
// Creating XML
function createXML() {
const xmlDoc = document.implementation.createDocument(null, 'users');
const root = xmlDoc.documentElement;
const user = xmlDoc.createElement('user');
user.setAttribute('id', '1');
const name = xmlDoc.createElement('name');
name.textContent = 'John Doe';
user.appendChild(name);
const email = xmlDoc.createElement('email');
email.textContent = 'john@example.com';
user.appendChild(email);
root.appendChild(user);
// Convert to string
const serializer = new XMLSerializer();
const xmlString = serializer.serializeToString(xmlDoc);
console.log(xmlString);
}
Working with JSON
JSON (JavaScript Object Notation) is the modern standard for data exchange. It's lightweight, readable, and natively supported by JavaScript.
JSON Structure
{
"users": [
{
"id": 1,
"name": "John Doe",
"email": "john@example.com",
"age": 30,
"active": true,
"roles": ["admin", "user"]
},
{
"id": 2,
"name": "Jane Smith",
"email": "jane@example.com",
"age": 28,
"active": true,
"roles": ["user"]
}
]
}
JSON Methods
// Parse JSON string to object
const jsonString = '{"name":"John","age":30}';
const obj = JSON.parse(jsonString);
console.log(obj.name); // "John"
// Convert object to JSON string
const user = {
name: 'John Doe',
email: 'john@example.com',
age: 30
};
const json = JSON.stringify(user);
console.log(json); // '{"name":"John Doe","email":"john@example.com","age":30}'
// Pretty print JSON
const prettyJson = JSON.stringify(user, null, 2);
console.log(prettyJson);
/*
{
"name": "John Doe",
"email": "john@example.com",
"age": 30
}
*/
// Filtering properties
const filtered = JSON.stringify(user, ['name', 'email']);
console.log(filtered); // '{"name":"John Doe","email":"john@example.com"}'
Handling Complex JSON
// Nested JSON
const data = {
users: [
{
id: 1,
name: 'John',
address: {
street: '123 Main St',
city: 'Boston',
country: 'USA'
}
}
]
};
// Access nested data
console.log(data.users[0].address.city); // "Boston"
// Safe navigation with optional chaining
const city = data.users?.[0]?.address?.city;
// Working with dates
const obj = {
name: 'Event',
date: new Date()
};
const json = JSON.stringify(obj);
console.log(json); // Dates become strings
const parsed = JSON.parse(json);
parsed.date = new Date(parsed.date); // Convert back to Date
XML vs JSON
| Aspect |
XML |
JSON |
| Size |
Larger (verbose) |
Smaller (compact) |
| Parsing |
Slower |
Faster |
| Data Types |
Text only |
Multiple types |
| Arrays |
Not native |
Native support |
| Comments |
Supported |
Not supported |
| Use Case |
Complex documents |
APIs, web apps |
Practical Examples
Example 1: Live Search
<input type="text" id="searchInput" placeholder="Search...">
<div id="results"></div>
<script>
const searchInput = document.getElementById('searchInput');
const resultsDiv = document.getElementById('results');
// Debounce function
function debounce(func, delay) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), delay);
};
}
// Search function
async function search(query) {
if (query.length < 2) {
resultsDiv.innerHTML = '';
return;
}
try {
const response = await fetch(`/api/search?q=${encodeURIComponent(query)}`);
const data = await response.json();
resultsDiv.innerHTML = data.map(item =>
`<div class="result-item">${item.name}</div>`
).join('');
} catch (error) {
console.error('Search failed:', error);
}
}
// Attach debounced search
searchInput.addEventListener('input', debounce(function() {
search(this.value);
}, 300));
</script>
Example 2: Loading Data on Scroll
<div id="content"></div>
<script>
let page = 1;
let loading = false;
async function loadMore() {
if (loading) return;
loading = true;
try {
const response = await fetch(`/api/posts?page=${page}`);
const posts = await response.json();
const content = document.getElementById('content');
posts.forEach(post => {
const div = document.createElement('div');
div.className = 'post';
div.innerHTML = `
<h3>${post.title}</h3>
<p>${post.content}</p>
`;
content.appendChild(div);
});
page++;
} catch (error) {
console.error('Load failed:', error);
} finally {
loading = false;
}
}
// Infinite scroll
window.addEventListener('scroll', () => {
if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 500) {
loadMore();
}
});
// Load initial content
loadMore();
</script>
Example 3: Form Submission with AJAX
<form id="contactForm">
<input type="text" name="name" required>
<input type="email" name="email" required>
<textarea name="message" required></textarea>
<button type="submit">Send</button>
</form>
<div id="formMessage"></div>
<script>
document.getElementById('contactForm').addEventListener('submit', async function(e) {
e.preventDefault();
const formData = new FormData(this);
const data = Object.fromEntries(formData);
const messageDiv = document.getElementById('formMessage');
try {
const response = await fetch('/api/contact', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
const result = await response.json();
if (response.ok) {
messageDiv.innerHTML = '<div class="success">Message sent!</div>';
this.reset();
} else {
messageDiv.innerHTML = `<div class="error">${result.error}</div>`;
}
} catch (error) {
messageDiv.innerHTML = '<div class="error">Network error</div>';
}
});
</script>
Example 4: Real-time Updates
// Poll server for updates
async function checkForUpdates() {
try {
const response = await fetch('/api/updates');
const data = await response.json();
if (data.hasUpdates) {
updateUI(data);
}
} catch (error) {
console.error('Update check failed:', error);
}
}
// Poll every 5 seconds
setInterval(checkForUpdates, 5000);
// Better: Use Server-Sent Events (SSE)
const eventSource = new EventSource('/api/updates/stream');
eventSource.onmessage = function(event) {
const data = JSON.parse(event.data);
updateUI(data);
};
eventSource.onerror = function(error) {
console.error('SSE error:', error);
eventSource.close();
};
Session Summary
Key Points
- AJAX: Asynchronous communication without page reloads
- XMLHttpRequest: Traditional API for AJAX requests
- Fetch API: Modern, promise-based alternative to XHR
- XML: Structured data format, verbose but flexible
- JSON: Lightweight data format, native to JavaScript
- Async/Await: Clean syntax for handling promises
- Error Handling: Always implement try-catch blocks
Best Practices
- Prefer Fetch API over XMLHttpRequest for new projects
- Use JSON instead of XML when possible
- Always handle errors and edge cases
- Implement loading states for better UX
- Debounce frequent requests (search, scroll)
- Set appropriate timeouts for requests
- Use async/await for cleaner code
Next Module Preview
In Module 3, we'll dive into PHP basics, learning server-side programming to build dynamic web applications with database integration and form processing.