Learning Objectives
- Handle
submiton the form — notclickon the button — and know why - Cancel navigation with
preventDefault()and keep the page - Read fields by hand:
value, checked radios, and selects - Read a whole form at once with
new FormData(form) - Lean on constraint validation:
checkValidity(),reportValidity()
Listen for submit, not click
A form submits in more ways than one: clicking the submit button, yes, but also pressing Enter in a text field. Listen for click on the button and you miss the keyboard path entirely. The submit event fires on the form for every path, which makes it the one true place to intervene:
preventDefault() here cancels the browser's default action — packaging the fields and navigating to the form's action URL. That default is the right behavior for a classic server-rendered form; cancel it only because you are taking over the job.
novalidate so the script decides when to check — but the checking itself is still the platform's. checkValidity() asks whether the required, type="email", and friends are satisfied; reportValidity() shows the browser's own error UI. No validation library required.Reading fields by hand
The manual approach reads each control through the DOM. Text inputs and textareas hand over value; a radio group needs a query for whichever one is :checked; a select's value is the chosen option:
The ?. matters: an untouched radio group has no checked member, and querying for one returns null. Note also what the demo does not do — echo the password back. It masks it before display. Small habit, right instinct.
Or let FormData do it
Field-by-field reading scales badly — every new control means another line of script. FormData reads the entire form in one constructor call, keyed by each control's name, exactly the way a server would receive it:
File inputs contribute real File objects, unchecked checkboxes contribute nothing (just like a real submission), and when the network enters the story, the same object goes straight out the door: fetch(url, { method: 'POST', body: formData }).
Applied
Two small, real uses of the same machinery. First, the classic intro-course exercise done the platform way: read a number, convert on click, announce the result through a live region — and guard the empty field, because Number('') is 0, not an error.
Second, autocomplete without a library: a <datalist> gives the input native suggestions, and one input listener echoes the value as it changes. Before reaching for an autocomplete widget, check whether the platform already ships one.
Next Steps
So far you have only listened to events the browser invented. In Step 6: Custom events, you mint your own:
- Create events with
new CustomEvent(type, { detail }) - Namespace your event names and route them through one listener
- Build components that announce themselves instead of being polled