De fleste HTML-formularer, jeg støder på i produktion, bruger tre inputtyper: text, password og submit. Det er det hele. Samtidig har HTML haft rige indbyggede inputtyper, begrænsningsvalidering og tilgængelighedsfunktioner i år — alle tilgængelige uden JavaScript og uden npm install. Lad os bruge dem.
Inputtyper Værd at Kende
Hver inputtype gør rigtigt arbejde for dig: den udløser det rigtige mobilklaviatur, giver indbygget validering og kommunikerer hensigten til hjælpeteknologier. Her er dem, der regelmæssigt dukker op i virkelige projekter:
<!-- Email: validates format, shows email keyboard on mobile -->
<input type="email" name="email" autocomplete="email">
<!-- Phone: shows numeric keypad on mobile -->
<input type="tel" name="phone" autocomplete="tel" pattern="[0-9]{10,15}">
<!-- Number: spin buttons, min/max/step validation -->
<input type="number" name="quantity" min="1" max="99" step="1" value="1">
<!-- Date: native date picker (no library needed) -->
<input type="date" name="birthdate" min="1900-01-01" max="2026-12-31">
<!-- Range: slider with min/max/step -->
<input type="range" name="volume" min="0" max="100" step="5" value="50">
<!-- Color: native color picker -->
<input type="color" name="theme_color" value="#0066cc">
<!-- File: single or multiple file upload -->
<input type="file" name="resume" accept=".pdf,.doc,.docx">
<input type="file" name="photos" accept="image/*" multiple>type="date" returnerer værdien i formatet YYYY-MM-DD uanset brugerens lokalitet — hvilket er præcis, hvad du ønsker, når du sender til en server. Parse aldrig datoer fra type="text"-inputs, hvis du kan undgå det.Etikettilknytning — Gør Det Rigtigt
Etiketter er ikke valgfri dekoration. De er den primære måde, skærmlæsere identificerer inputs på, og at klikke på en etiket bør fokusere det tilknyttede input. Der er to gyldige tilgange:
<!-- Method 1: for/id association (most common, most flexible) -->
<label for="email">Email address</label>
<input type="email" id="email" name="email">
<!-- Method 2: wrapping label (no id needed) -->
<label>
Email address
<input type="email" name="email">
</label>
<!-- Wrong: placeholder is NOT a label -->
<input type="email" name="email" placeholder="Email address">
<!-- placeholder disappears when the user starts typing — terrible UX for screen readers -->Tilgangen med for/id er normalt at foretrække, fordi det holder etiketter og inputs afkoblede i DOM, hvilket er lettere at style. Stol aldrig på placeholder som etikettsubstitut — det svigter brugere med kognitive handicap og forsvinder i det øjeblik, nogen begynder at skrive. W3C WAI:s etikettevejledning og WebAIM's vejledning om formularkontroller dækker tilgængeligheds-begrundelsen mere dybdegående.
Indbygget Begrænsningsvalidering
HTML's indbyggede begrænsningsvalidering kører inden formularindsendelse og er genuint kraftfuld. Du får meget nyttigt adfærd gratis:
<form>
<!-- required: field must have a value -->
<label for="fullname">Full name</label>
<input type="text" id="fullname" name="fullname" required>
<!-- minlength/maxlength: character count constraints -->
<label for="username">Username (3–20 chars)</label>
<input type="text" id="username" name="username"
minlength="3" maxlength="20" required>
<!-- pattern: regex-based validation -->
<label for="postcode">UK Postcode</label>
<input type="text" id="postcode" name="postcode"
pattern="[A-Z]{1,2}[0-9][0-9A-Z]?s?[0-9][A-Z]{2}"
title="Enter a valid UK postcode (e.g. SW1A 1AA)"
required>
<!-- min/max on date -->
<label for="checkin">Check-in date</label>
<input type="date" id="checkin" name="checkin"
min="2026-04-16" required>
<button type="submit">Submit</button>
</form>required. Feltet skal ikke være tomt. På afkrydsningsfelter skal det være markeret.pattern. Et regex, som værdien skal matche. Tilføj altid ettitle-attribut — det bliver valideringsværktøjstip-teksten og giver brugerne et hint om det forventede format.minlength/maxlength. Antal tegn til tekstinputs.maxlengthafskærer stille input;minlengthvaliderer kun ved indsendelse.min/max. Numeriske eller datobaserede grænser. Virker pånumber-,date-,range- ogtime-inputs.step. Definerer gyldige inkrementer.step="0.01"på et valutafelt tillader ører.step="any"deaktiverer trin-kontrollen helt.
fieldset og legend til Gruppering
Når du har en gruppe relaterede inputs — særligt radioknapper eller afkrydsningsfelter — grupperer <fieldset> og <legend> dem semantisk. Skærmlæsere læser legenden inden hvert input i gruppen, så brugerne altid ved, hvilet spørgsmål en radioknap besvarer.
<form>
<fieldset>
<legend>Preferred contact method</legend>
<label>
<input type="radio" name="contact" value="email" checked>
Email
</label>
<label>
<input type="radio" name="contact" value="phone">
Phone
</label>
<label>
<input type="radio" name="contact" value="post">
Post
</label>
</fieldset>
<fieldset>
<legend>Notification preferences</legend>
<label>
<input type="checkbox" name="notify_new_posts" value="1">
New articles
</label>
<label>
<input type="checkbox" name="notify_replies" value="1">
Replies to my comments
</label>
</fieldset>
</form>API for Begrænsningsvalidering
API for begrænsningsvalidering giver dig JavaScript-adgang til den samme valideringslogik, som browseren bruger nativt. Det lader dig udløse validering programmatisk og sætte tilpassede fejlmeddelelser uden at blokere indsendelse:
const usernameInput = document.getElementById('username');
// Check if a single field is valid
console.log(usernameInput.checkValidity()); // true or false
console.log(usernameInput.validity.tooShort); // true if below minlength
console.log(usernameInput.validity.patternMismatch); // true if pattern fails
// Set a custom error message (shows in the browser's native validation tooltip)
usernameInput.setCustomValidity('That username is already taken.');
usernameInput.reportValidity(); // triggers the tooltip immediately
// Clear a custom error (important — once set, it sticks until cleared)
usernameInput.setCustomValidity('');
// Validate on the fly as the user types (async example — username availability)
usernameInput.addEventListener('input', async () => {
const value = usernameInput.value;
if (value.length < 3) return; // let minlength handle this
const response = await fetch(`/api/check-username?q=${encodeURIComponent(value)}`);
const { available } = await response.json();
usernameInput.setCustomValidity(available ? '' : 'Username is already taken.');
});Bemærk den kritiske detalje: når du kalder setCustomValidity() med en ikke-tom streng, er feltet permanent ugyldigt, indtil du rydder det ved at kalde setCustomValidity(''). At glemme rydningstrinnet er den mest almindelige fejl, når man bruger dette API.
novalidate til Tilpasset JS-validering
Hvis du bygger et tilpasset valideringsgrænseflade med styliserede fejlmeddelelser (ikke browserens native tooltips), tilføj novalidate til formularen. Det deaktiverer browser-native validering, mens API for begrænsningsvalidering holdes tilgængeligt for dine egne tjek:
<form id="signup-form" novalidate>
<div class="field">
<label for="signup-email">Email</label>
<input type="email" id="signup-email" name="email" required>
<span class="error" aria-live="polite"></span>
</div>
<div class="field">
<label for="signup-password">Password</label>
<input type="password" id="signup-password" name="password" minlength="8" required>
<span class="error" aria-live="polite"></span>
</div>
<button type="submit">Create account</button>
</form>const form = document.getElementById('signup-form');
form.addEventListener('submit', (e) => {
e.preventDefault();
let isValid = true;
form.querySelectorAll('input').forEach((input) => {
const errorEl = input.nextElementSibling;
if (!input.checkValidity()) {
isValid = false;
errorEl.textContent = input.validationMessage;
input.setAttribute('aria-invalid', 'true');
} else {
errorEl.textContent = '';
input.removeAttribute('aria-invalid');
}
});
if (isValid) {
form.submit();
}
});autocomplete og aria-describedby
To attributter, der markant forbedrer formular-UX og er nemme at overse:
<!-- autocomplete: tells browsers/password managers what the field is for -->
<input type="text" name="fname" autocomplete="given-name">
<input type="text" name="lname" autocomplete="family-name">
<input type="email" name="email" autocomplete="email">
<input type="tel" name="phone" autocomplete="tel">
<input type="password" name="password" autocomplete="current-password">
<input type="password" name="new_pass" autocomplete="new-password">
<input type="text" name="cc" autocomplete="cc-number">
<!-- aria-describedby: links an input to a hint or error message -->
<label for="pass">Password</label>
<input
type="password"
id="pass"
name="password"
minlength="8"
aria-describedby="pass-hint"
required
>
<span id="pass-hint">Must be at least 8 characters.</span>Attributtet autocomplete bruger standardiserede tokenværdier defineret af WHATWG-specifikationen. Når du bruger det korrekte token, kan browsere og adgangskodeadministratorer udfylde felter pålideligt automatisk. aria-describedby forbinder et hint eller fejlmeddelelse med et input — skærmlæsere læser beskrivelsen efter feltetiketten, hvilket gør begrænsninger hørbare, inden brugeren overhovedet begynder at skrive.
En Komplet Tilgængelig Login-formular
Her er alt kombineret — en login-formular, der fungerer for tastaturbrugere, skærmlæserbrugere og mobilbrugere, ved hjælp af kun HTML og en lille smule JavaScript:
<form id="login-form" novalidate>
<h2>Sign in</h2>
<div class="field">
<label for="login-email">Email address</label>
<input
type="email"
id="login-email"
name="email"
autocomplete="email"
aria-describedby="email-error"
required
>
<span id="email-error" class="error" aria-live="polite" role="alert"></span>
</div>
<div class="field">
<label for="login-password">Password</label>
<input
type="password"
id="login-password"
name="password"
autocomplete="current-password"
aria-describedby="password-error"
required
>
<span id="password-error" class="error" aria-live="polite" role="alert"></span>
<a href="/forgot-password">Forgot password?</a>
</div>
<label class="inline">
<input type="checkbox" name="remember" value="1">
Keep me signed in for 30 days
</label>
<button type="submit">Sign in</button>
</form>Vigtige punkter: aria-live="polite" på fejlelementer betyder, at skærmlæsere vil annoncere fejl, når de vises, uden at afbryde det, brugeren aktuelt hører. role="alert" forstærker dette for ældre skærmlæsere. Værdierne for autocomplete matcher, hvad adgangskodeadministratorer forventer, så autoudfyldning fungerer korrekt ved første forsøg.
Nyttige Værktøjer
Når du bygger og tester HTML-formularer, fanger HTML-validatoren strukturfejl som manglende etiketter og duplikerede ID'er, inden brugerne gør det. Til generel HTML-oprydning holder HTML-formatering din kode læsbar. MDN's input-elementreference er den mest komplette dokumentation for alle inputtyper og deres attributter.
Opsummering
HTML-formularer har langt mere indbygget kapacitet, end de fleste projekter udnytter. At vælge den rigtige inputtype giver dig mobilklaviatur, validering og semantisk mening gratis. Korrekt etikettilknytning og aria-describedby gør formularer navigerbare uden mus. API for begrænsningsvalidering giver dig JavaScript-kroge til native validering uden at kæmpe mod browseren. Sæt disse dele sammen, og du får formularer, der fungerer for alle — inden du skriver en eneste linje af tilpasset valideringslogik.