Handling Forms
Forms have a built-in submission behavior: clicking the submit button sends data to a server and reloads the page. With JavaScript you intercept that submission, read the values, validate them, and decide what to do next.
The submit event
Section titled “The submit event”Attach the submit event listener to the form element, not the button:
const form = document.querySelector('.contact-form');
form.addEventListener('submit', (event) => { event.preventDefault(); // stop the page reload console.log('Form submitted');});event.preventDefault() is essential here — without it the browser sends the request and reloads, discarding all your JavaScript.
If your form fields have required attributes, add novalidate to the <form> element. Without it, the browser intercepts the submit and shows its own validation popups before your JavaScript runs:
<form class="contact-form" novalidate>Your JavaScript validation then owns all error messaging.
Reading field values
Section titled “Reading field values”After preventing the default, read field values with event.target.value or by selecting the input directly:
form.addEventListener('submit', (event) => { event.preventDefault();
const nameField = form.querySelector('#name'); const emailField = form.querySelector('#email'); const messageField = form.querySelector('#message');
const name = nameField.value.trim(); const email = emailField.value.trim(); const message = messageField.value.trim();
console.log({ name, email, message });});Always .trim() text values — users frequently add accidental whitespace.
Required field validation
Section titled “Required field validation”Check all required fields before acting on the data:
form.addEventListener('submit', (event) => { event.preventDefault();
const name = form.querySelector('#name').value.trim(); const email = form.querySelector('#email').value.trim(); const message = form.querySelector('#message').value.trim();
const errors = [];
if (!name) { errors.push('Name is required.'); }
if (!email) { errors.push('Email is required.'); } else if (!email.includes('@')) { errors.push('Email must contain @.'); }
if (!message) { errors.push('Message is required.'); }
if (errors.length > 0) { console.log('Errors:', errors); return; // stop here — do not submit }
console.log('Valid — ready to submit:', { name, email, message });});The else if on the email check runs only when the email field is not empty — avoids duplicate errors.
Showing inline error messages
Section titled “Showing inline error messages”Logging to the console is not user-facing. Show errors in the page using textContent:
Each form group needs a <span class="field-error"> immediately after its input. The STO contact.html already has these in place — one per field, with ids matching the pattern name-error, email-error, message-error:
<div class="form-group"> <label for="contact-name">Your name</label> <input type="text" id="contact-name" name="name" required> <span class="field-error" id="name-error"></span></div>You also need a success element to show after a valid submission. Add this after the closing </form> tag in contact.html:
<div class="form-success" id="form-success"> Thank you! Your message has been sent. We'll be in touch shortly.</div>Add the following to styles.css in the form section:
.field-error { display: block; font-size: 0.875rem; color: var(--color-error); margin-top: var(--space-xs); min-height: 1.25em;}
.form-success { display: none; padding: var(--space-md); background-color: var(--color-accent); color: var(--color-primary); border-radius: 4px; margin-top: var(--space-md); font-weight: 600;}
.form-success.visible { display: block;}Then define two helper functions:
function showError(fieldId, message) { const errorEl = document.querySelector(`#${fieldId}-error`); if (errorEl) errorEl.textContent = message;}
function clearErrors() { document.querySelectorAll('.field-error').forEach(el => { el.textContent = ''; });}Call clearErrors() at the top of the submit handler to reset any previous errors, then showError(fieldId, message) for each problem found.
STO capstone: contact form validation
Section titled “STO capstone: contact form validation”Full validation handler for the STO contact form:
const contactForm = document.querySelector('.contact-form');const successMessage = document.querySelector('.form-success');
if (contactForm) { contactForm.addEventListener('submit', (event) => { event.preventDefault(); clearErrors();
const name = contactForm.querySelector('#contact-name').value.trim(); const email = contactForm.querySelector('#contact-email').value.trim(); const message = contactForm.querySelector('#contact-message').value.trim();
let hasErrors = false;
if (!name) { showError('name', 'Name is required.'); hasErrors = true; }
if (!email) { showError('email', 'Email is required.'); hasErrors = true; } else if (!email.includes('@')) { showError('email', 'Enter a valid email address.'); hasErrors = true; }
if (!message) { showError('message', 'Message is required.'); hasErrors = true; } else if (message.length > 500) { showError('message', 'Message must be 500 characters or fewer.'); hasErrors = true; }
if (hasErrors) return;
contactForm.reset(); updateCharCount(0); if (successMessage) successMessage.classList.add('visible'); });}Note: updateCharCount(0) resets the character counter display when the form clears. This function was defined in the previous lesson’s character counter code — both blocks live in main.js and share scope.
Test by:
- Submitting the empty form — all three error messages should appear
- Filling only name and email — message error should appear
- Entering an email without
@— email error should show - Typing more than 500 characters in the message — over-limit error should show on submit
- Filling all fields correctly — success message should appear and form should clear
- Attach
submitto the form element, not the submit button. - Call
event.preventDefault()immediately to stop the page reload. - Read field values with
.value.trim()— always trim to remove accidental whitespace. - Validate all required fields and return early if any exist. Use
showError()andclearErrors()helpers to keep the handler clean. - Show errors inline using
textContent— neveralert(). - Reset the form and show a success message when all validation passes.