Skip to content
EzzyWeb

Level 3, Interactive Building, Chapter 12 of 24

Forms & Validation

Why this matters

Forms are how the web collects information: signing up for an account, checking out of a cart, posting a comment. But a form that silently accepts garbage, an empty name field, a malformed email, a password that's too short, creates frustration and, worse, bad data. Good forms catch problems early and explain clearly what needs to change, right where the user is looking.

You already know how to build form markup and listen for the submit event. This chapter connects those skills: reading values out of form fields with JavaScript, checking them against rules, and giving immediate, specific feedback, all before any data goes anywhere else.

The lesson

Reading values from form fields

Every form input has a value property that holds its current contents as a string, even for number inputs, so you'll often need to convert it with Number() when you need to do arithmetic with it. Checkboxes and radio buttons instead expose a checked property, true or false, since their meaningful state isn't text. You can grab any of these with querySelector, exactly as you learned in the DOM chapter, and read or set their value property just like you would textContent on other elements.

Reading values on submit
JavaScript
1const form = document.querySelector("#signup-form");23form.addEventListener("submit", (event) => {4  event.preventDefault();5  const email = document.querySelector("#email").value;6  const agree = document.querySelector("#agree").checked;7  console.log(email, agree);8});
preventDefault stops the page from reloading, giving your JavaScript full control over what happens with the submitted data.

Built-in HTML validation

HTML itself provides basic validation attributes you can add directly to inputs: required prevents submission if a field is empty, type="email" checks for a roughly valid email shape, minlength and maxlength constrain text length, and min and max constrain numeric ranges. These run automatically when a form is submitted, showing the browser's built-in error messages, and cost you nothing extra in JavaScript. They're a great first line of defense, but they can't check things specific to your app, like whether a username is already taken or whether two password fields match, which is where JavaScript validation comes in.

Built-in validation attributes
HTML
1<input type="email" required />2<input type="password" minlength="8" required />3<input type="number" min="1" max="10" />
These attributes alone stop obviously invalid submissions before any JavaScript needs to run.

Custom validation with JavaScript

For rules HTML can't express, you write your own checks in the submit handler, after calling preventDefault, and decide what to do based on the result. A common pattern is to build up a list of error messages using conditionals, then, if that list is non-empty, stop and display the errors instead of proceeding. This is exactly the same conditional logic you learned in Level 2, applied specifically to values read out of form fields. Comparing a password field to a confirm-password field, checking that a chosen username has no spaces, or verifying an age is above a minimum are all typical examples that require this kind of custom logic.

Custom validation logic
JavaScript
1form.addEventListener("submit", (event) => {2  event.preventDefault();3  const password = document.querySelector("#password").value;4  const confirm = document.querySelector("#confirm").value;5  const errors = [];67  if (password.length < 8) {8    errors.push("Password must be at least 8 characters.");9  }10  if (password !== confirm) {11    errors.push("Passwords do not match.");12  }1314  if (errors.length > 0) {15    console.log(errors);16    return;17  }1819  console.log("Form is valid, submitting...");20});
Collecting every error into an array lets you report all problems at once instead of stopping at the first one.

Showing feedback in the page

Logging errors to the console is fine for testing, but real users need to see feedback directly in the page. The pattern is familiar from the DOM chapter: select or create an element near the relevant field, set its textContent to the error message, and use classList to add a visible error style, then remove that class again once the field becomes valid. Doing this per-field, rather than one big error message at the top of the form, helps people fix problems faster because the feedback appears right next to the thing that needs attention.

  • HTML attributes like required, type="email", and minlength catch simple problems with no JavaScript.
  • value reads a text input's current contents; checked reads a checkbox or radio button's state.
  • Custom rules that HTML can't express, like matching two fields, need JavaScript inside a submit handler.
  • Collecting all errors before showing feedback lets users fix everything in one pass instead of one at a time.
Displaying an error message near a field
JavaScript
1const emailInput = document.querySelector("#email");2const emailError = document.querySelector("#email-error");34if (!emailInput.value.includes("@")) {5  emailError.textContent = "Please enter a valid email address.";6  emailInput.classList.add("invalid");7} else {8  emailError.textContent = "";9  emailInput.classList.remove("invalid");10}
The error text and the invalid class both get cleared once the field is corrected, so feedback never lingers unnecessarily.

Try it yourself

Build a small signup form and validate it on submit: check that the username isn't empty and that the password is at least 8 characters, showing a clear error message for each problem.

Live preview

Chapter boss project

Boss project: A Fully Validated Signup Form

Build a signup form with a username, email, password, and confirm-password field. Combine HTML validation attributes with custom JavaScript checks so that every field is validated both structurally and against your app-specific rules, like the two passwords matching.

Every invalid field should show a specific, clear error message right next to it, and gain a visible 'invalid' style. Once a field becomes valid again, its error message and style should disappear immediately rather than waiting for another submit attempt.

Only when every single check passes should the form log a success message, simulating a real submission. Think carefully about what happens on submit versus what could happen live as someone types, though checking only on submit is a perfectly good starting point.

  • Start with HTML attributes (required, type="email", minlength) for the basics, then layer your JavaScript checks on top for anything HTML can't express.
  • Write one small validation check at a time and test it alone before adding the next one.
  • Store each error paragraph's element in a variable up front so you're not repeatedly calling querySelector inside the submit handler.
  • For the matching-passwords check, compare the two fields' values directly with !== inside your submit handler.

Level-up checklist

Tick these off once each one is true for you.