JavaScript and forms
Handle the submit event
Listen for form submission at the form level, prevent the default only when needed, and preserve native behavior when JavaScript fails.
When you add JavaScript to a form, listen to the form’s submit event. Not the button’s click event.
Here is the basic shape:
const form = document.querySelector('form')
form.addEventListener('submit', event => {
event.preventDefault()
const data = event.submitter
? new FormData(form, event.submitter)
: new FormData(form)
console.log([...data])
})
The handler runs every time the form is about to submit. Then it builds a FormData from the form, including the button that triggered it, and logs the entries.
Why the form and not the button
A form can submit in more ways than you’d think. A click on the submit button. Enter pressed inside a text field. A second submit button with a different value. A call to form.requestSubmit() from other code. All of those fire submit on the form. A click listener on one button only sees the first one.
I’ve seen forms where the “Save” button worked but pressing Enter sent the data unvalidated to the server. That’s the bug a form-level listener avoids.
preventDefault is a decision
event.preventDefault() stops the browser from doing its normal thing, which is navigating to the form’s action with the form’s method. Only call it when your script is going to complete the submission some other way, for example with fetch().
If you just want to log, or add a field, or run an extra check, leave the default alone. The native submission is a good baseline. It works, it’s accessible, and you didn’t have to write it.
Keep the HTML complete
Even with JavaScript handling the submission, write the form as if the script didn’t exist:
<form action="/feedback" method="post">
<!-- labeled controls -->
<button type="submit">Send feedback</button>
</form>
The action and method are still there. If the script fails to load, or throws before attaching the listener, the form still reaches the server. JavaScript can make the experience nicer with inline results. It should never be the only place that knows where the data goes.
Validation still belongs to the server
form.checkValidity() returning true inside the handler tells you the browser is happy. It doesn’t tell you the data is safe. The handler runs in a browser the visitor controls, and anyone can call the endpoint without it. The server validates again, every time.
Try this: add two submit buttons named action with values draft and publish, and log event.submitter in the handler. Submit once by clicking “Publish” and once by pressing Enter in a text field. Check which value shows up in the FormData each time.
Lesson completed