Reading form values, validating user input, showing error and success messages, preventing submission, and giving every user clear, accessible feedback
A form is a promise. It tells a visitor: type something here, and the page will do something useful with it. Every contact form, sign-up form, search box, and comment field makes this same quiet promise. But a form that accepts input without ever responding to it breaks that promise. If a user leaves a required field empty, types an email address with no "@" symbol, or clicks submit and nothing visibly happens, the form has failed — not because the HTML was wrong, but because the user was never told what was happening.
This chapter teaches you how JavaScript closes that gap. You will learn how to read the values a user has typed or selected, how to check those values against simple rules, how to show clear error messages when something is wrong, how to show success messages when everything checks out, and how to stop a form from submitting until it is ready. You will also learn that good feedback is not only visual — it must reach users who rely on screen readers and keyboards, and it must never assume that raw user input is automatically safe to display. By the end of the chapter, you will be able to build a small but genuinely usable validated form, the kind found throughout the real web.
Imagine two versions of the same sign-up form. In Version A, a user leaves the email field empty, clicks "Submit," and the page does nothing at all — no message, no color change, no sound. In Version B, the same action instantly produces a message next to the email field: "Please enter your email address." Both forms have identical HTML. What makes Version B work and Version A fail? The answer is feedback — and feedback, on the modern web, is built with JavaScript. This chapter shows you how.
By the end of this chapter, you should be able to:
event.preventDefault() to stop a form from submitting when validation fails.aria-live regions, aria-describedby, and aria-invalid..value and .checked properties; reading text, checkbox, radio, and select values; reading values fresh inside an event handlerrequired attribute and its limitssubmit event; event.preventDefault(); combining validation with prevention; why this is not the same as server securityaria-live regions; aria-describedby; aria-invalid; making sure feedback reaches screen reader and keyboard userstextContent versus innerHTML; client-side validation is not securityA form is one of the few places on a website where the conversation runs both ways. Most of a web page simply presents information to a visitor. A form asks the visitor a question — a name, an email address, a message — and waits for an answer. Like any conversation, it goes wrong quickly if one side stops responding.
Consider what happens when a form gives no feedback at all. A user fills in three of four fields, forgets the fourth, and clicks "Submit." If nothing happens — no message appears, no field is highlighted, the page simply sits there — the user has no way to know what went wrong, or even that anything went wrong. They may click again. They may give up. They may assume the website is broken.
Feedback is any information returned to a user about the state of their action. Good forms give feedback at several moments: while the user is typing, immediately after they try to submit, and clearly when the action succeeds. JavaScript is the tool that makes this kind of responsive, moment-by-moment feedback possible in the browser.
HTML already gives some feedback on its own. Browsers display a small pop-up bubble when a required field is empty or an email-type field is malformed. This built-in feedback is useful, but it looks different in every browser, cannot be styled to match your page, and offers limited control over wording or timing. JavaScript validation builds on top of these HTML basics to give you consistent, well-designed, fully controllable feedback.
Before JavaScript can check anything about a form, it has to know what the user actually typed, clicked, or chose. Every form control stores its current state in a property that JavaScript can read directly. The property you read depends on the kind of control.
| Control | Property to Read | What It Returns |
|---|---|---|
| Text, email, password, textarea | .value | The text currently typed, as a string |
| Checkbox | .checked | true or false |
| Radio button | .checked on the selected option | true for the chosen option in the group, false for the others |
| Select menu | .value | The value of the currently chosen option |
| Number input | .value (as text) or .valueAsNumber | A string by default; convert to a number when needed |
Notice that nameValue is read at one specific moment. If the user keeps typing afterward, that variable does not update on its own — it holds the value as it was when the line ran. This is why form values are almost always read inside an event handler, at the moment they are needed, rather than once when the page first loads.
Text values often need a small cleanup step before they are checked. Users sometimes leave stray spaces at the beginning or end of what they type, especially after copying and pasting. The string method .trim() removes that leading and trailing whitespace without changing the meaningful content in the middle.
Always read values fresh, at the moment you need them. A common beginner mistake is to read a form's values once, near the top of a script, and then check that old copy later. By the time the user clicks Submit, the stored copy may no longer match what is actually in the field. Read the value inside the function that responds to the relevant event — typically the submit handler — so you always check what the user currently sees on the screen.
The simplest and most common validation check is also the most important one: has the user actually entered anything at all? A required field is a field that must contain a value before the form can be considered complete.
HTML provides a built-in required attribute that marks a field this way and triggers the browser's default warning bubble. That attribute is valuable and you should keep using it. But once you are writing your own JavaScript feedback, you need a way to perform the same check yourself, so that you control exactly what happens when a required field is empty.
Real forms usually have several required fields at once. Rather than writing a separate if statement for each one, it is common to collect the fields that need checking and loop through them, applying the same empty-check to each.
Browsers also expose a built-in method called checkValidity(), which asks an input whether it currently satisfies its HTML validation attributes (required, type="email", minlength, and others). This chapter focuses on writing your own checks so you understand exactly what is happening, but it is worth knowing that checkValidity() exists once you are comfortable with the fundamentals.
Checking only value === '' misses fields that contain nothing but spaces, which look empty to a human but are technically a non-empty string to JavaScript. Always trim before comparing, as shown in the isEmpty() function above.
Once you can confirm that a field is not empty, the next question is whether its content makes sense. A validation rule is a small, specific test that a value must pass — and like an algorithm, a validation rule is the idea, while the JavaScript code that checks it is the implementation. The same rule, "this field must be a valid-looking email address," can be checked in slightly different ways, but the underlying rule stays the same.
Most beginner-level web forms rely on a small set of recurring rule patterns.
password.length >= 8.email.includes('@') combined with a length check, or a basic regular expression such as /^\S+@\S+\.\S+$/.password === confirmPassword.age >= 13 && age <= 120.HTML itself offers a first layer of these same rules through attributes such as minlength, maxlength, min, max, pattern, and type="email". These attributes are valuable and should generally be kept in your markup even when you also validate with JavaScript — they provide a baseline of feedback for every browser, including situations where your script has not yet finished loading.
You do not need to memorize a complicated email-validating regular expression. A simple check that looks for the basic shape of an email address — some text, an "@" symbol, more text, a period, and more text — is appropriate for an introductory course. No simple pattern catches every invalid address, and real applications generally confirm an email address by sending an actual message to it, not just by inspecting its shape.
Detecting an invalid value is only half the job. The other half is telling the user, clearly and specifically, what is wrong and how to fix it. A common pattern is to reserve a small, empty element near each input — often a <span> or <p> with a matching id — and fill that element's text whenever validation fails.
This pattern depends on matching HTML, where each input has a sibling element ready to hold its message:
A message like "Invalid input" tells the user that something is wrong but not what or how to fix it. Specific, friendly wording — "Please enter your email address" or "Password must be at least 8 characters" — respects the user's time and reduces frustration far more effectively than a generic warning.
Error messages tell a user what went wrong. Success messages tell a user that everything went right — and they matter just as much. Without a clear confirmation, a user who has correctly filled out a form is left wondering whether their submission actually worked.
A few practical habits make success messages more effective. Place the message somewhere the user is already looking — near the top of the form or directly above the submit button, rather than somewhere they would have to scroll to find. Clear any leftover error messages at the same time you show success, so the page does not display contradictory feedback. And keep the wording calm and specific: "Thank you — your message has been sent" tells the user exactly what happened.
In a real application connected to a server, the success message usually appears only after the server confirms that the data was received and processed — not the instant the JavaScript finishes running on the page. Chapter 28 introduces fetch(), which is how a page eventually learns whether a server-side action actually succeeded.
By default, clicking a form's submit button does something dramatic: the browser gathers up the form's data and navigates to a new page, even if no server is actually listening on the other end. For a form validated entirely in JavaScript, this default behavior gets in the way — the page would reload before your error messages ever had a chance to appear.
The submit event fires the moment a form is about to be submitted, and the event object it provides has a method, preventDefault(), that cancels that default navigation. This gives your JavaScript full control: check the values, and only allow the "real" submission to proceed — or, in a practice exercise with no server, show a success message instead — once everything is valid.
In the examples throughout this chapter, there is no real server receiving the form's data, so preventDefault() simply stops a pointless page reload and lets the success message take its place. In a production application connected to a server, client-side validation and preventDefault() only improve the experience for honest users typing into a browser. A user can disable JavaScript entirely or send data directly to the server through other tools, bypassing every check this chapter teaches. That is why every real web application must also validate data again on the server — a topic for later, full-stack courses. Client-side validation is about user experience; server-side validation is about safety and correctness.
A red border and a small caption of text communicate an error clearly to a sighted user reading the page visually. They communicate nothing at all to a person using a screen reader, unless the page explicitly tells the assistive technology what changed. Accessible form feedback is feedback that reaches every user, regardless of how they are experiencing the page.
"polite" waits for a natural pause before announcing.id of its error or help text, so a screen reader reads that text aloud whenever the user focuses the field — connecting the message to the exact field it concerns."true" on an input that has failed validation, and removed or set to "false" once it is corrected, so assistive technology can announce the field's current state.With this markup in place, the same showError() function from Section 24.5 now does double duty: setting the error span's text content both displays the message visually and triggers a screen reader announcement, because the span is marked as a live region. A small addition to your JavaScript — toggling aria-invalid alongside your existing CSS class — completes the picture.
Accessible feedback is not an advanced extra step reserved for specialists. The same handful of attributes shown above — aria-live, aria-describedby, and aria-invalid — cover the overwhelming majority of everyday form feedback. Building the habit now, in small introductory forms, makes it automatic in larger projects later.
Whenever your JavaScript displays text that a user typed, it has a choice about how to insert that text into the page. That choice matters more than it might first appear.
The DOM property textContent inserts text exactly as plain text — whatever the user typed appears on the page as visible characters, even if it happens to contain something that looks like an HTML tag. The property innerHTML, by contrast, interprets its contents as HTML markup. If a user's input is inserted with innerHTML and that input happens to contain HTML or script-like content, the browser may render it as part of the page rather than as plain text — which can break the page's appearance or, in more serious cases, run unintended code.
Prefer textContent whenever you are displaying text that came from a user. Reserve innerHTML for content you wrote yourself or have deliberately built and trust, such as a template string composed entirely of your own fixed markup with no raw user text inserted directly into it. This single habit prevents the large majority of introductory-level input-handling mistakes.
This chapter does not expect you to become a security expert. The goal at this introductory level is a single durable habit: treat any value typed by a user as plain text unless you have a specific, deliberate reason to do otherwise, and never assume client-side checks alone make a form "safe." Real protection always also happens on the server, which later courses in this series cover in depth.
A form that works the one time you tried it while writing the code is not the same as a form that works. Testing a validated form means deliberately trying to break it — checking every kind of input a real user, careless or otherwise, might provide.
Chapter 29 returns to testing and debugging in greater depth, alongside accessibility and performance review, once you have more components and patterns to test together. The habits introduced here — trying empty input, boundary values, and keyboard-only navigation — apply to every interactive feature you build for the rest of this book.
.value for text-like inputs and select menus, .checked for checkboxes and radio buttons. Values should be read fresh, inside an event handler, at the moment they are needed.value.trim() === '') reliably detects empty or whitespace-only fields.submit event and event.preventDefault() let JavaScript stop a form's default page reload so validation can run first. Client-side prevention improves user experience but is not a substitute for server-side validation.aria-live, aria-describedby, and aria-invalid connect visual feedback to screen reader users, ensuring error and success messages reach everyone, not only sighted users who can see a red border.textContent rather than innerHTML avoids treating raw input as markup. Client-side checks protect user experience, not security; real protection requires server-side validation as well.Answer these questions before beginning the full review questions. They check basic understanding, not deep analysis.
isEmpty() function need to do besides comparing the value to an empty string, and why?event.preventDefault() do when called inside a form's submit handler?textContent usually safer than innerHTML when displaying user-typed text?required attribute, I don't need JavaScript validation."required attribute gives a useful baseline warning, but its appearance and wording differ across browsers and cannot be styled to match your page. JavaScript validation lets you control exactly what message appears, where it appears, and how it looks — and lets you add rules, such as matching fields or custom formats, that required alone cannot express.aria-describedby — so the message reaches every user, not only those who can see and interpret the color.preventDefault() permanently disables the form."preventDefault() only cancels the default behavior for that one occurrence of the event — the single click or keypress that triggered the handler. The form remains fully functional, and the user can try again; the next submit attempt fires its own fresh submit event, which your handler will check just as before.These activities ask you to write or extend small validation rules. Trace each rule by hand with a few sample values before testing it in a browser.
isValidPassword(password) that returns true only if the password is at least 8 characters long. Test it by hand with passwords of 5, 7, 8, and 12 characters.passwordsMatch(password, confirmPassword) that returns true only if the two values are exactly equal. Explain why trimming is usually not appropriate for this particular rule.isValidAge(age) that returns true only if the numeric age is between 13 and 120, inclusive. Identify what should happen if the input cannot be converted to a number at all.Read each code snippet carefully and answer the question that follows before running anything in a browser.
showError() and clearError() functions from Section 24.5. What would happen if a developer called showError() but forgot to ever call the matching clearError() once the field becomes valid?formIsValid starts as true and is only set to false inside an if block. Explain why the code is written this way rather than starting as false.display.innerHTML = commentText; to show a user's comment on the page. Explain the risk in this line and rewrite it more safely.Review a form you have built, or a form from a real website, against the following criteria.
<label>, connected with a matching for and id?aria-describedby?aria-live so a screen reader announces new errors automatically?aria-invalid="true" set, and is it removed once the field becomes valid?Each scenario describes a form that is not behaving correctly. Identify the likely cause before looking at any code.
submit event listener has clearly been written. What single line is probably missing from the handler?textContent and innerHTML?required and type="email" even after you add your own JavaScript validation?innerHTML and why textContent avoids that risk.console.log() to be considered finished.Build a contact form with three fields — name, email, and message — plus a submit button. The form should meet every requirement below before you consider it complete.
event.preventDefault() so the page never reloads.aria-describedby, and the input's aria-invalid attribute is set and removed as its validity changes.event.preventDefault() inside a submit handler to stop a page reload.aria-live, aria-describedby, and aria-invalid.textContent over innerHTML when displaying user-typed text.Chapter 24 focused on a single, self-contained interaction: validating one form and responding to it. Chapter 25 opens Part V, where you begin combining HTML, CSS, and JavaScript into larger, reusable building blocks. You will learn to design hero sections, content sections, cards, callouts, and feature lists as repeatable patterns, using consistent CSS classes and spacing so the same design choices can appear across many pages without being rewritten each time. The validated contact form you just built will return as one of several reusable sections in your running Student Learning Site project.