AJAX with Forms
Submitting Forms with AJAX
By default, submitting an HTML form causes a full page reload. Using AJAX, you can intercept the submit event with preventDefault(), collect the form data, and send it to the server in the background — giving users instant feedback without a page reload.
document.getElementById('contact-form').addEventListener('submit', async function (e) {
e.preventDefault(); // stop the default page reload
const form = e.target;
const submitBtn = form.querySelector('button[type="submit"]');
const statusEl = document.getElementById('form-status');
// Show loading state
submitBtn.disabled = true;
submitBtn.textContent = 'Sending...';
statusEl.textContent = '';
// Collect form data as a plain object
const data = {
name: form.name.value.trim(),
email: form.email.value.trim(),
message: form.message.value.trim()
};
// Basic client-side validation
if (!data.name || !data.email || !data.message) {
statusEl.textContent = 'Please fill in all fields.';
statusEl.className = 'text-danger';
submitBtn.disabled = false;
submitBtn.textContent = 'Send';
return;
}
try {
const res = await fetch('/api/contact', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (!res.ok) throw new Error(`Server error: ${res.status}`);
statusEl.textContent = 'Message sent successfully!';
statusEl.className = 'text-success';
form.reset();
} catch (err) {
statusEl.textContent = `Failed to send: ${err.message}`;
statusEl.className = 'text-danger';
} finally {
submitBtn.disabled = false;
submitBtn.textContent = 'Send';
}
});
FormData Object
The FormData API automatically serializes all form fields — including file inputs — into a format suitable for sending as a multipart request. You do not need to set Content-Type manually when using FormData with fetch(); the browser sets it automatically with the correct boundary.
document.getElementById('upload-form').addEventListener('submit', async function (e) {
e.preventDefault();
// FormData automatically captures all fields including file inputs
const formData = new FormData(e.target);
// You can also append extra fields manually
formData.append('uploadedAt', new Date().toISOString());
// Inspect FormData entries (for debugging)
for (const [key, value] of formData.entries()) {
console.log(key, value);
}
const progressBar = document.getElementById('upload-progress');
try {
// DO NOT set Content-Type header — browser sets it with boundary automatically
const res = await fetch('/api/upload', {
method: 'POST',
body: formData
});
if (!res.ok) throw new Error(`Upload failed: ${res.status}`);
const result = await res.json();
console.log('Uploaded file URL:', result.url);
document.getElementById('upload-status').textContent = 'Upload complete!';
} catch (err) {
console.error(err);
document.getElementById('upload-status').textContent = err.message;
}
});
// Check if username is available as the user types
const usernameInput = document.getElementById('username');
const usernameStatus = document.getElementById('username-status');
let debounceTimer;
usernameInput.addEventListener('input', function () {
const username = this.value.trim();
clearTimeout(debounceTimer); // reset timer on each keystroke
if (username.length < 3) {
usernameStatus.textContent = 'Username must be at least 3 characters.';
usernameStatus.className = 'text-warning';
return;
}
usernameStatus.textContent = 'Checking...';
// Debounce: wait 400ms after user stops typing before sending request
debounceTimer = setTimeout(async () => {
try {
const res = await fetch(`/api/check-username?username=${encodeURIComponent(username)}`);
const data = await res.json();
if (data.available) {
usernameStatus.textContent = '✓ Username is available';
usernameStatus.className = 'text-success';
} else {
usernameStatus.textContent = '✗ Username is already taken';
usernameStatus.className = 'text-danger';
}
} catch {
usernameStatus.textContent = 'Could not check availability';
usernameStatus.className = 'text-muted';
}
}, 400);
});
Ready to Level Up Your Skills?
Explore 500+ free tutorials across 20+ languages and frameworks.