1
Fork 0
form-validation/app.js
Leonardo Devai befab0e70d Review and modernize all 42 projects to the updated standard
Pinned 2026 toolchains (Go 1.26, Rust 1.98/edition 2024, Python 3.14 + uv, Node 24,
Zig 0.16, NixOS 26.05), postgres 18 / mongo 8, lockfiles built from, non-root
runtimes, .dockerignore, per-project LICENSE, READMEs with the git.devai.io clone
line, checkout@v7 CI. Security fixes in the legacy Rust APIs (any-password login,
self-assigned admin, hard-coded JWT secret), JWT alg/exp/sub enforcement across the
blog series, safe markdown links in the frontends, and many smaller bugs — every
project was built, run and exercised end to end.

Adds scripts/publish.sh + a CI publish job that splits every folder into its own
repo at git.devai.io/templates/<folder>.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01128fhuZbgivaSJvtMf4s1G
2026-09-27 21:10:38 +02:00

58 lines
2.3 KiB
JavaScript

const form = document.getElementById("form");
const successEl = document.getElementById("success");
// Every field is described in one place: its input, the <p> for its error, and
// a validate rule that returns an error message — or "" when the value is fine.
const fields = {
name: {
input: document.getElementById("name"),
error: document.getElementById("nameError"),
validate: (value) => (value.trim() ? "" : "Please enter your name."),
},
email: {
input: document.getElementById("email"),
error: document.getElementById("emailError"),
// Deliberately simple: something@something.something. The real test of an
// address is sending it an email.
validate: (value) =>
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) ? "" : "That doesn't look like an email.",
},
password: {
input: document.getElementById("password"),
error: document.getElementById("passwordError"),
validate: (value) => (value.length >= 8 ? "" : "Use at least 8 characters."),
},
confirm: {
input: document.getElementById("confirm"),
error: document.getElementById("confirmError"),
validate: (value) =>
value === fields.password.input.value ? "" : "Passwords don't match.",
},
};
// Run one rule and show the result right under its field. aria-invalid marks
// the input as wrong for screen readers, and the input's aria-describedby
// (in index.html) makes them read the message out too.
function checkField(field) {
const message = field.validate(field.input.value);
field.error.textContent = message;
field.input.setAttribute("aria-invalid", message ? "true" : "false");
return message === "";
}
// Check a field when you leave it ("blur"): early feedback, without nagging
// while you're still typing.
for (const field of Object.values(fields)) {
field.input.addEventListener("blur", () => checkField(field));
}
// On submit, check every field so ALL the errors show at once, then move the
// cursor to the first one that needs fixing.
form.addEventListener("submit", (event) => {
event.preventDefault(); // this demo only validates; nothing is sent anywhere
const invalid = Object.values(fields).filter((field) => !checkField(field));
successEl.textContent = invalid.length === 0 ? "All good! ✓" : "";
if (invalid.length > 0) invalid[0].input.focus();
});