20. Forms and Inputs
The browser validates inputs for you. You can also check from JavaScript using `form.checkValidity()` or each element's `validity` object.
What you'll learn
- 1Distinguish input, change, and submit events.
- 2Use built-in HTML5 validation (required, type, pattern).
- 3Collect all form values at once with `FormData`.
- 4Control and validate inputs from JavaScript.
- 5Use the preventDefault + fetch pattern for submission.
Overview
The browser validates inputs for you. You can also check from JavaScript using `form.checkValidity()` or each element's `validity` object.
Core Concepts
1. input vs change vs submit
- `input`: every time the value changes (on every keystroke for text)
- `change`: when the value is committed after focus loss (text), or on each change (select/checkbox)
- `submit`: when a form is submitted
form.addEventListener("submit", (e) => {
e.preventDefault();
// handle the data
});2. HTML5 validation
<input type="email" required minlength="3" />The browser validates inputs for you. You can also check from JavaScript using `form.checkValidity()` or each element's `validity` object.
3. FormData
const data = new FormData(form);
data.get("email");
for (const [k, v] of data) console.log(k, v);`FormData` also handles file uploads transparently.
4. Controlled inputs
The pattern where JavaScript always owns the value and reflects it back to the input.
input.addEventListener("input", (e) => {
state.value = e.target.value.toUpperCase();
input.value = state.value;
});5. Submission pattern
form.addEventListener("submit", async (e) => {
e.preventDefault();
if (!form.checkValidity()) {
form.reportValidity();
return;
}
const data = new FormData(form);
// Submit with fetch (covered in the next lecture)
});Examples
| File | What it covers |
|---|---|
| index.html | Example form |
| 01_input_submit.js | input and submit events |
| 02_validation.js | Using HTML5 validation |
| 03_formdata.js | Using FormData |
| 04_controlled.js | JS-controlled values |
src/01_input_submit.js
/**
* input and submit events.
*/
const form = document.querySelector("#loginForm");
const email = form.querySelector("[name=email]");
email.addEventListener("input", (e) => {
console.log("typing:", e.target.value);
});
form.addEventListener("submit", (e) => {
e.preventDefault();
console.log("form submitted");
});
src/02_validation.js
/**
* Use the HTML5 validation API.
*/
const form = document.querySelector("#loginForm");
form.addEventListener("submit", (e) => {
if (!form.checkValidity()) {
e.preventDefault();
form.reportValidity();
console.log("validation failed");
} else {
console.log("validation passed");
}
});
src/03_formdata.js
/**
* Collect all values at once with FormData.
*/
const form = document.querySelector("#loginForm");
form.addEventListener("submit", (e) => {
e.preventDefault();
if (!form.checkValidity()) {
form.reportValidity();
return;
}
const data = new FormData(form);
for (const [key, value] of data) {
console.log(`${key} = ${value}`);
}
});
src/04_controlled.js
/**
* JS controls the value (force uppercase).
*/
const username = document.querySelector("[name=username]");
const echo = document.querySelector("#echo");
username.addEventListener("input", (e) => {
const upper = e.target.value.toUpperCase();
e.target.value = upper;
echo.textContent = `current value: ${upper}`;
});
src/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Forms and inputs</title>
</head>
<body>
<form id="loginForm">
<label>Email <input type="email" name="email" required /></label>
<label>Password <input type="password" name="password" required minlength="4" /></label>
<label>Name <input type="text" name="username" /></label>
<label><input type="checkbox" name="remember" /> Keep me logged in</label>
<button type="submit">Submit</button>
</form>
<p id="echo"></p>
<script src="01_input_submit.js"></script>
<script src="02_validation.js"></script>
<script src="03_formdata.js"></script>
<script src="04_controlled.js"></script>
</body>
</html>
Common Mistakes
- Forgetting `preventDefault` in `submit` and getting a page reload.
- Reading `e.value` instead of `e.target.value`.
- Mixing up `.checked` (checkbox) and `.value` (text).
- Forgetting to give inputs a `name` and getting nothing back from FormData.
- Trying to enforce domain rules (like password matching) with HTML5 alone.
FAQ
Q1. checkValidity vs reportValidity?
`checkValidity` only checks; `reportValidity` also shows a message to the user when invalid.
Q2. Are controlled inputs always best?
They have a rendering cost on every keystroke β only use them when needed.
Q3. Can file inputs use FormData?
Yes β encoding is automatically `multipart/form-data`.
Practice
Build an email + password form. On submit, dump the values via FormData. Block empty submissions.
homework/README.md
Build a form with email and password fields:
- Block the default reload on submit.
- Collect values via FormData and log them.
- Block submission when any field is empty.
See `answer/homework_01.html` and `answer/homework_01.js`.
homework/answer/homework_01.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Homework 20-1</title>
</head>
<body>
<form id="login">
<label>Email <input type="email" name="email" required /></label>
<label>Password <input type="password" name="password" required /></label>
<button type="submit">Log in</button>
</form>
<script src="homework_01.js"></script>
</body>
</html>
homework/answer/homework_01.js
/**
* Dump form values via FormData on submit.
*/
const form = document.querySelector("#login");
form.addEventListener("submit", (event) => {
event.preventDefault();
if (!form.checkValidity()) {
form.reportValidity();
return;
}
const data = new FormData(form);
for (const [key, value] of data) {
console.log(`${key}: ${value}`);
}
});
Next Lecture
[21_Fetch_and_JSON](../../06_λΈλΌμ°μ _API/21_Fetchμ_JSON/) β Talking to a server.
All lecture materials and example code are openly available on GitHub.
View on GitHub β