Forms and validation
Reading what a visitor typed, stopping the page from reloading, and answering with a message that helps.
The form that reloads the page
A form submits by default: the browser gathers the fields and navigates away. That was the web’s original design, and it is exactly what a script needs to stop if it wants to answer in place.
event.preventDefault() in the submit handler cancels the navigation and leaves the page where it is. Every form-handling script starts with it, and a form that "flashes" instead of answering is almost always missing it.
That default is older than JavaScript and still useful: a form without a script submits to a URL and the server answers with a new page. Once a script takes over, the navigation is no longer wanted, but the browser cannot know that. Cancelling it is a deliberate act, which is why the call is explicit.
Read, judge, answer
const form = document.querySelector("form");
const input = document.querySelector("#email");
const message = document.querySelector(".message");
form.addEventListener("submit", (event) => {
event.preventDefault();
const email = input.value.trim();
if (email.includes("@")) {
message.textContent = `Thanks — we will write to ${email}.`;
} else {
message.textContent = "That does not look like an email address.";
}
});The value of a field
input.value is what is in the field right now, as text — even for a field that looks like a number. .trim() removes the spaces around it, because " [email protected] " is the same email with a leading space, and comparisons care.
After a successful submit, clearing the field (input.value = "") is the small courtesy that tells the visitor their input was taken. It is the difference between a form that answered and one that seems to have eaten what was typed.
Reading the field at submit time is important. A value captured when the script loaded would be the empty initial value forever, no matter what the visitor typed. The handler runs later, so it reads the current contents; that is the whole reason the read belongs inside the handler.
Validation that helps
A good message says what is wrong and what to do about it, in the same breath: "That does not look like an email — it needs an @ sign." Helpful beats formal, and specific beats polite.
Let the browser do the first pass where it can: type="email" and required in the HTML catch the common cases before the script runs. The script is for the rules HTML cannot express and for the message the visitor actually reads.
Validation the browser already knows
Long before your script runs, HTML can state some rules: required, type="email", minlength="8", min="1" and max="12". The browser checks them on submit and refuses to send an invalid form, showing its own message at the first field that fails.
form.checkValidity() runs the same checks and returns true or false without showing anything; form.reportValidity() shows the messages. A script that wants to take over the conversation can add novalidate to the form, but it is usually better to let the browser handle the simple rules and add only the checks HTML cannot express.
Every field is read in its own way
A text or number field is read with .value, and that value is always a string — even when the field has type="number". A checkbox is read with .checked, a boolean; its .value is the string "on" unless the HTML sets something else. A select is read with .value too, which is the selected option’s value.
That variety is the reason to read one field at a time and log it while building. Number(input.value) is the conversion for arithmetic, and input.value.trim() is the one for comparisons — a form is mostly text until you decide otherwise.
A worked submit, traced
Suppose the visitor types [email protected] and presses Join. The browser fires submit on the form before it navigates. The handler calls preventDefault, so the navigation is cancelled, then reads input.value, trims it and tests for an @. The test passes, and the handler writes a sentence into the message paragraph.
If the test fails, the else branch writes the other message and the field keeps its text, so the visitor can correct it without retyping. If preventDefault were missing, the browser would navigate away at the end of the handler and neither message would be on screen long enough to read.
Three mistakes: treating a number field’s .value as a number, so "5" + 1 becomes "51"; reading a checkbox with .value instead of .checked, which gives "on" whatever the box says; and forgetting event.preventDefault(), so the browser navigates away and the message you wrote disappears with the page.
Try these next
- Log
form.checkValidity()before and after filling a required field. - Add
minlength="4"to a field and submit a shorter value to see the browser’s own message. - Add a checkbox, then log both its
.checkedand its.valueto compare them.
A form that answers in place
The form reloads when it is submitted. Stop that, read the email field, and write a friendly message on the page — accepting anything with an @ in it.
3 left to do
- The form listens for submit
- The page is stopped from reloading
- The field’s value is read
Listen for submit on the form, call event.preventDefault(), read input.value.trim(), and set message.textContent.
An order with a checkbox
Read the number of loaves and the gluten-free checkbox, then write an order summary into the paragraph. Treat the number field as text until you convert it.
3 left to do
- The page is stopped from reloading
- The field text is converted to a number
- The checkbox is read with checked
Number(document.querySelector("#loaves").value) gives you a number; document.querySelector("#gluten").checked is a boolean. Build the sentence with a template literal and set the paragraph’s textContent.
Write the message for the person, not the developer: say what to change, not which check failed. “Add an @ to the address” is a fix; “invalid input” is a shrug.
Check what you learned
One question at a time. A wrong answer explains itself — take another go.
Part 1 of 5
What happens when a submit handler does not call preventDefault()?
