Tutorials Logic, IN +91 8092939553 info@tutorialslogic.com
FAQs Support
Navigation
Home About Us Contact Us Blogs FAQs
Tutorials
All Tutorials
Services
Academic Projects Resume Writing Interview Questions Website Development
Compiler Tutorials

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.

Basic AJAX Form Submission
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.

FormData — Including File Uploads
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;
  }
});
Real-Time Form Validation with AJAX
// 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.