Üretimde karşılaştığım HTML formlarının büyük çoğunluğu üç girdi türü kullanıyor: text, password ve submit. Hepsi bu. Oysa HTML, yıllardır zengin yerleşik girdi türlerine, kısıtlama doğrulamaya ve erişilebilirlik özelliklerine sahip — hepsi JavaScript ve npm install gerektirmeden kullanılabilir. Bunları kullanalım.
Bilmeye Değer Girdi Türleri
Her girdi türü sizin için gerçek iş yapar: doğru mobil klavyeyi tetikler, yerleşik doğrulama sağlar ve yardımcı teknolojilere niyeti iletir. Gerçek projelerde sürekli karşılaşılan türler bunlardır:
<!-- 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", kullanıcının yerel ayarından bağımsız olarak değeri YYYY-MM-DD biçiminde döndürür — sunucuya gönderirken tam olarak istediğiniz şey bu. Mümkünse type="text" girdilerinden tarih ayrıştırmaktan kaçının.Etiket İlişkilendirmesi — Doğru Yapın
Etiketler isteğe bağlı süsleme değildir. Ekran okuyucuların girdileri tanımlamasının birincil yoludur ve bir etikete tıklamak ilişkili girdiye odaklanmalıdır. İki geçerli yaklaşım vardır:
<!-- 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 -->for/id yaklaşımı genellikle tercih edilir çünkü etiketleri ve girdileri DOM'da birbirinden bağımsız tutar, bu da stil vermesi daha kolaydır. placeholder'ı asla etiket yerine kullanmayın — bilişsel engelli kullanıcıları zorlar ve kullanıcı yazmaya başlar başlamaz kaybolur. W3C WAI etiket öğreticisi ve WebAIM form denetim kılavuzu, erişilebilirlik gerekçesini daha ayrıntılı ele alır.
Yerleşik Kısıtlama Doğrulaması
HTML'nin yerleşik kısıtlama doğrulaması, form gönderilmeden önce çalışır ve gerçekten güçlüdür. Birçok faydalı davranışı ücretsiz olarak alırsınız:
<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. Alan boş olamaz. Onay kutularında işaretlenmiş olmalıdır.pattern. Değerin eşleşmesi gereken bir regex. Her zamantitleözelliği ekleyin — doğrulama araç ipucu metni olur ve kullanıcılara beklenen biçim hakkında ipucu verir.minlength/maxlength. Metin girdileri için karakter sayısı.maxlengthgirdiyi sessizce keser;minlengthyalnızca göndermede doğrular.min/max. Sayısal veya tarih sınırları.number,date,rangevetimegirdilerinde çalışır.step. Geçerli artışları tanımlar. Bir para birimi alanındastep="0.01"kuruşlara izin verir.step="any"adım kontrolünü tamamen devre dışı bırakır.
Gruplama için fieldset ve legend
İlgili girdi grubunuz varsa — özellikle radyo düğmeleri veya onay kutuları — <fieldset> ve <legend> bunları anlamsal olarak gruplar. Ekran okuyucular, gruptaki her girdiden önce açıklamayı okur; böylece kullanıcılar hangi soruya radyo düğmesiyle yanıt verdiklerini her zaman bilir.
<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>Kısıtlama Doğrulama API'si
Kısıtlama Doğrulama API'si, tarayıcının yerel olarak kullandığı doğrulama mantığına JavaScript erişimi sağlar. Bu sayede doğrulamayı programlı olarak tetikleyebilir ve gönderimi engellemeden özel hata mesajları ayarlayabilirsiniz:
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.');
});Kritik ayrıntıya dikkat edin: setCustomValidity()'yi boş olmayan bir dizeyle çağırdıktan sonra, setCustomValidity('') çağrısıyla temizlenene kadar alan kalıcı olarak geçersiz kalır. Temizleme adımını unutmak bu API'yi kullanırken yapılan en yaygın hatadır.
Özel JS Doğrulaması için novalidate
Stilli hata mesajlarıyla özel bir doğrulama kullanıcı arayüzü oluşturuyorsanız (tarayıcının yerel araç ipuçları değil), forma novalidate ekleyin. Bu, Kısıtlama Doğrulama API'sini kendi kontrolleriniz için kullanılabilir tutarken tarayıcının yerel doğrulamasını devre dışı bırakır:
<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 ve aria-describedby
Form kullanıcı deneyimini önemli ölçüde iyileştiren ve kolayca gözden kaçırılan iki özellik:
<!-- 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>autocomplete özelliği, WHATWG spesifikasyonu tarafından tanımlanan standartlaştırılmış token değerleri kullanır. Doğru token kullandığınızda tarayıcılar ve parola yöneticileri alanları güvenilir şekilde otomatik doldurabilir. aria-describedby, bir ipucunu veya hata mesajını bir girdiyle ilişkilendirir — ekran okuyucular açıklamayı alan etiketinden sonra okur, bu da kısıtlamaların kullanıcı yazmaya başlamadan önce duyulmasını sağlar.
Tam Erişilebilir Giriş Formu
İşte hepsi bir arada — yalnızca HTML ve küçük miktarda JavaScript kullanarak klavye kullanıcıları, ekran okuyucu kullanıcıları ve mobil kullanıcılar için çalışan bir giriş formu:
<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>Temel noktalar: hata span'larındaki aria-live="polite", ekran okuyucuların hatalar göründüğünde kullanıcının o sırada dinlediklerini kesmeden duyurması anlamına gelir. role="alert" bunu eski ekran okuyucular için pekiştirir. autocomplete değerleri, parola yöneticilerinin beklediğiyle eşleşir; bu sayede otomatik doldurma ilk denemede doğru çalışır.
Faydalı Araçlar
HTML formları oluştururken ve test ederken HTML Validator, kullanıcılar bulmadan önce eksik etiketler ve tekrarlanan kimlikler gibi yapısal hataları yakalar. Genel HTML temizleme için HTML Formatter işaretlemenizi okunabilir tutar. MDN input öğesi başvurusu, tüm girdi türleri ve özellikleri için en kapsamlı dokümantasyondur.
Özet
HTML formları, çoğu projenin kullandığından çok daha fazla yerleşik kapasiteye sahiptir. Doğru girdi türünü seçmek size mobil klavyeleri, doğrulamayı ve anlamsal anlam ücretsiz olarak kazandırır. Uygun etiket ilişkilendirmesi ve aria-describedby, formları fare olmadan gezilebilir kılar. Kısıtlama Doğrulama API'si, tarayıcıyla savaşmadan yerel doğrulamaya JavaScript kancaları sağlar. Bu parçaları bir araya getirin ve tek bir satır özel doğrulama mantığı yazmadan önce herkes için çalışan formlar elde edersiniz.