Forms & Validation
Form এখনো user data পাঠানোর প্রধান পথ
1. Submit + FormData
form.addEventListener("submit", e => {
e.preventDefault(); // stop the page reload
const data = new FormData(form);
// Read individual fields
console.log(data.get("name"));
console.log(data.get("email"));
// Iterate everything
for (const [k, v] of data.entries()) console.log(k, v);
// Send to server
fetch("/api/signup", { method: "POST", body: data });
});
FormData form-এর প্রতিটি field collect করে। সরাসরি fetch-এর body-তে পাঠানো যায় — multipart/form-data automatically সেট হয়।2. HTML5 Constraint Attributes
<input name="email" type="email" required>
<input name="age" type="number" min="13" max="120" required>
<input name="name" minlength="2" maxlength="60" required>
<input name="phone" pattern="\+?880\d{10}" placeholder="+8801XXXXXXXXX">
<input name="url" type="url">
<input name="agree" type="checkbox" required>
The browser checks these on submit and refuses to send if any field fails. Pair with JS for a polished UX.
3. Programmatic Validation API
// On every form
form.checkValidity(); // boolean
form.reportValidity(); // also shows native error bubbles
// Per input
input.validity.valueMissing
input.validity.typeMismatch
input.validity.tooShort
input.validity.patternMismatch
input.validity.customError
// Set a custom error
if (passwords.value !== confirm.value) {
confirm.setCustomValidity("Passwords don't match");
} else {
confirm.setCustomValidity("");
}
4. Sandbox-Safe Validator
const rules = {
name: v => v.length >= 2 || "name too short",
email: v => /^\S+@\S+\.\S+$/.test(v) || "invalid email",
phone: v => /^\+?880\d{10}$/.test(v) || "invalid BD phone",
age: v => (+v >= 13 && +v <= 120) || "age 13–120",
};
function validate(data) {
const errors = {};
for (const [k, fn] of Object.entries(rules)) {
const r = fn(data[k] ?? "");
if (r !== true) errors[k] = r;
}
return errors;
}
console.log(validate({
name: "Arif", email: "a@x.com",
phone: "+8801712345678", age: "22"
}));
console.log(validate({
name: "A", email: "bad", phone: "123", age: "5"
}));
5. Accessibility
- Always wrap inputs in
<label>or usefor=""/id=""linking - Set
aria-invalid="true"on bad fields andaria-describedbypointing to the error message - Use
autocompleteattributes (e.g.autocomplete="email") so password managers help - Disable the submit button while sending to prevent double-submit
- Always re-validate on the server — never trust the browser alone
6. Working Form Skeleton
<form id="signup" novalidate>
<label>Email
<input name="email" type="email" required autocomplete="email">
<span class="err" aria-live="polite"></span>
</label>
<label>Phone
<input name="phone" pattern="\+?880\d{10}" required>
<span class="err" aria-live="polite"></span>
</label>
<button type="submit">Sign up</button>
</form>
<script>
const form = document.getElementById("signup");
form.addEventListener("submit", async (e) => {
e.preventDefault();
let valid = true;
form.querySelectorAll("input").forEach(input => {
const err = input.nextElementSibling;
if (!input.checkValidity()) {
err.textContent = input.validationMessage;
valid = false;
} else err.textContent = "";
});
if (!valid) return;
const res = await fetch("/api/signup",
{ method: "POST", body: new FormData(form) });
if (res.ok) form.reset();
});
</script>
7. Glossary (শব্দকোষ)
| Term | Meaning | বাংলায় |
|---|---|---|
FormData | Auto-collects all named inputs of a form into a key/value structure. | Form-এর সব named input automatically সংগ্রহ করে। |
submit event | Fires when the user submits a form; preventDefault to handle in JS. | Form submit-এর event; JS-এ handle করতে preventDefault দিন। |
| Constraint validation | HTML5 attributes (required, minlength, pattern) checked by the browser. | HTML5 attribute দিয়ে browser-এর built-in validation। |
checkValidity | Returns true/false without showing UI. | UI না দেখিয়ে valid কিনা চেক করে। |
reportValidity | Like checkValidity but also shows native error bubbles. | Validity দেখায় এবং native error bubble পপ-আপ করে। |
setCustomValidity | Set a custom error message on an input. | Custom error message নির্দিষ্ট করার API। |
aria-invalid | Marks an input as failing validation for screen readers. | Screen reader-কে জানায় input invalid। |
aria-live | Announces dynamic text changes (good for error messages). | Dynamic text change-কে assistive tech announce করে। |
novalidate | Disables native validation UI so you can render your own. | Browser-এর built-in UI বন্ধ — নিজের UI দেখাতে। |
autocomplete | Hints to password managers / autofill (e.g. email). | Password manager / autofill-কে hint। |
FormData + fetch সবচেয়ে পরিষ্কার pattern; aria-invalid ও aria-live দিয়ে accessibility সুনিশ্চিত করুন। সবসময় submit চলাকালীন button disable করুন — double-submit আটকাতে।
8. Practice Problems
- Build a runnable validator that checks an email string.
✨ Show Answer
a1.jsconst isEmail = s => /^\S+@\S+\.\S+$/.test(s); console.log(isEmail("a@x.com"), isEmail("nope")); - Validate Bangladeshi phone numbers (
+8801XXXXXXXXX).✨ Show Answer
a2.jsconst isBdPhone = s => /^\+?880\d{10}$/.test(s); ["+8801712345678", "01712345678", "+88017"] .forEach(p => console.log(p, isBdPhone(p))); - Convert a FormData to a plain object (sketch).
✨ Show Answer
const data = Object.fromEntries(new FormData(form)); - Validate a strong password (≥8 chars, 1 number, 1 symbol).
✨ Show Answer
a4.jsconst strong = s => s.length >= 8 && /\d/.test(s) && /[!@#$%^&*()]/.test(s); ["hi!", "abcdef12", "abcdef1!"].forEach(p => console.log(p, strong(p))); - Build a "passwords match" custom validator (sketch).
✨ Show Answer
function check() { const m = pw.value === confirm.value; confirm.setCustomValidity(m ? "" : "Passwords don't match"); } [pw, confirm].forEach(i => i.addEventListener("input", check)); - Why must validation also run on the server?
✨ Show Answer
Answer: The browser is fully under user control — they can disable JS, edit the HTML, send raw HTTP. Client-side validation is a UX optimisation; the server is the only trustworthy gatekeeper for invariants like uniqueness, ownership, and limits.
- Build an aria-live error display that updates without focus loss (sketch).
✨ Show Answer
<span id="err" aria-live="polite"></span> input.addEventListener("input", () => { err.textContent = input.checkValidity() ? "" : input.validationMessage; }); - Disable submit while a request is in flight (sketch).
✨ Show Answer
btn.disabled = true; try { await fetch(...); } finally { btn.disabled = false; } - Validate a credit-card-shaped string with the Luhn algorithm (logic only).
✨ Show Answer
a9.jsconst luhn = s => { let sum = 0, alt = false; for (let i = s.length - 1; i >= 0; i--) { let d = +s[i]; if (alt) { d *= 2; if (d > 9) d -= 9; } sum += d; alt = !alt; } return sum % 10 === 0; }; console.log(luhn("4539578763621486")); - Why use
novalidateon a form when you do JS validation?✨ Show Answer
Answer: The native error bubbles look ugly and inconsistent across browsers. Adding
novalidatetells the browser to skip its own UI — you callcheckValidity/reportValidityprogrammatically and render errors with your own styling. - Build a min/max age validator that returns specific messages.
✨ Show Answer
a11.jsconst ageError = a => { if (Number.isNaN(+a)) return "not a number"; if (a < 13) return "too young"; if (a > 120) return "too old"; return null; }; ["hi", 5, 22, 200].forEach(a => console.log(a, ageError(a))); - In one paragraph, explain why
FormDatais preferable to manualinput.valuereads.✨ Show Answer
Answer:
FormDataauto-collects every named input, supports files (which JSON cannot), respects checkbox/radio semantics, and can be sent directly as the body of a fetch request — the browser handles the multipart encoding for you. Manual reads scale badly and are easy to forget when fields are added.
Summary — Module 27
Forms collect user input. Use FormData + fetch to send. Pair HTML5 attributes with JS validators for great UX, but always re-validate on the server. Tag invalid inputs with aria-invalid and explain errors via aria-live.