Intermediate · JavaScript in the browser: DOM and events · topic 37 of 64 · 35 min
Forms with JavaScript
Explanation
Taking over the submit
When a user presses the submit button, the form fires a submit event. By default, the browser sends the data and loads a new page. To check the data first, or to send it with fetch, listen for submit and call event.preventDefault().
<form id="signup" novalidate>
<label for="mobile">Mobile number</label>
<input id="mobile" name="mobile" type="tel" inputmode="numeric"
autocomplete="tel" required aria-describedby="mobile-error">
<p id="mobile-error" class="error"></p>
<label for="pin">PIN code</label>
<input id="pin" name="pin" inputmode="numeric" required aria-describedby="pin-error">
<p id="pin-error" class="error"></p>
<button type="submit">Create account</button>
</form>novalidate on the form turns off the browser's own error bubbles, because we will show our own messages. Keep required and type in the HTML anyway: they help screen readers and phone keyboards.
Reading all values with FormData
new FormData(form) collects the value of every field that has a name attribute. Object.fromEntries() turns it into a plain object like { mobile: '9876543210', pin: '560001' }. All values are strings.
const form = document.querySelector('#signup');
form.addEventListener('submit', (event) => {
event.preventDefault(); // stop the page reload
const data = Object.fromEntries(new FormData(form));
const errors = validateForm(data);
showErrors(form, errors);
if (Object.keys(errors).length === 0) {
sendForm(form, data);
}
});Your own validation rules
| Field | Rule | Check in JavaScript |
|---|---|---|
| Mobile (India) | 10 digits, starts with 6, 7, 8 or 9 | /^[6-9]\d{9}$/.test(mobile) |
| PIN code | 6 digits, does not start with 0 | /^[1-9]\d{5}$/.test(pin) |
| Password | At least 8 characters | password.length >= 8 |
| Confirm password | Same as password | confirm === password |
function validateForm(data) {
const errors = {};
if (!/^[6-9]\d{9}$/.test(data.mobile)) {
errors.mobile = 'Enter a 10-digit mobile number';
}
if (!/^[1-9]\d{5}$/.test(data.pin)) {
errors.pin = 'Enter a 6-digit PIN code';
}
if (data.password.length < 8) {
errors.password = 'Use at least 8 characters';
}
if (data.confirm !== data.password) {
errors.confirm = 'The passwords do not match';
}
return errors; // {} means everything is fine
}Showing errors next to the field
Put each message in the box that belongs to its field. aria-describedby on the input links it to that box, so a screen reader reads the error together with the label. aria-invalid="true" tells it the field is wrong. setCustomValidity(message) marks the field invalid for the browser too, and setCustomValidity('') clears it.
function showErrors(form, errors) {
for (const input of form.querySelectorAll('input')) {
const message = errors[input.name] || '';
const box = document.getElementById(input.getAttribute('aria-describedby'));
if (box) box.textContent = message;
input.setCustomValidity(message); // '' means valid
input.setAttribute('aria-invalid', message ? 'true' : 'false');
}
}Disabling the button while sending
async function sendForm(form, data) {
const button = form.querySelector('button[type=submit]');
button.disabled = true; // stop double clicks
button.textContent = 'Sending...';
try {
await fetch('/api/signup', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
} finally {
button.disabled = false; // runs on success AND on error
button.textContent = 'Create account';
}
}Watch the lesson
Forms with JavaScript · 1/7
Forms are where users give you data: sign-ups, bookings, payments. JavaScript makes them friendly.
Prefer a video by a teacher? Find free videos on “javascript form validation formdata tutorial for beginners” on YouTube ↗
In real life: A railway or bus booking form
A ticket booking form asks for passenger name, mobile number and PIN code. When you press "Continue", JavaScript stops the reload, reads all fields with FormData, and finds the mobile number has only 9 digits. It shows "Enter a 10-digit mobile number" right under that box and moves focus there. Once everything is right, the button changes to "Please wait..." and is disabled, so a nervous double tap does not book two tickets.
Practice (0/3 done)
Write the markup for a sign-up form that JavaScript can work with. Inside a <form id="signup">: a <label for="mobile">, an <input id="mobile" name="mobile" type="tel" required> with aria-describedby pointing to an error box such as <p id="mobile-error">, and a submit <button>. No script is needed here.
Write validateForm(data). data has name, mobile, pin, password and confirm (all strings). Return an errors object with a message string for each wrong field, and {} when all is fine. Rules: name is not empty after trim(); mobile is 10 digits starting with 6, 7, 8 or 9; pin is 6 digits not starting with 0; password has at least 8 characters; confirm equals password. Use the keys name, mobile, pin, password, confirm.
Build a full sign-up form in your browser with name, mobile, PIN code, password and confirm password. On submit: stop the reload, read the values with FormData, run your validateForm, show each error under its field, and move focus to the first wrong field. When everything is right, disable the button for 2 seconds (use setTimeout to pretend you are sending) and then show "Account created".
Quick quiz
Read more (free)
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.