Abdulaziz Akyol

HTML5 formlar: input tipleri, doğrulama ve autocomplete rehberi

HTML5 · Yazılım geliştirme
1 Ekim 2018 · 6 dk okuma · Abdulaziz Akyol

HTML5 formları, kullanıcıdan veri almak için gereken girdi tiplerini, doğrulama kurallarını ve otomatik doldurma ipuçlarını doğrudan HTML’e taşır. Doğru tipi ve öznitelikleri seçtiğinizde tarayıcı mobilde uygun klavyeyi açar, hatalı girişi gönderimden önce yakalar ve kayıtlı adres ya da parolaları doğru alanlara doldurur; üstelik bunların çoğu için tek satır JavaScript gerekmez.

Bu rehber, 2018’de yazdığım altı ayrı form notunun birleştirilmiş ve düzeltilmiş hâlidir. Eski notlarda başlıktaki “HTLM” yazım hatasından type="name" gibi var olmayan bir tipe, multipart/formdata gibi hatalı değerlerden standarttan çıkarılmış <keygen> öğesine kadar düzeltilmesi gereken çok şey vardı. Serinin genel çerçevesi için HTML5 nedir? yazısına bakabilirsiniz.

Bir formun temel yapısı nasıl olmalı?

<form action="/iletisim" method="post">
  <p>
    <label for="ad">Ad soyad</label>
    <input id="ad" name="ad" type="text" autocomplete="name" required>
  </p>
  <p>
    <label for="eposta">E-posta</label>
    <input id="eposta" name="eposta" type="email" autocomplete="email" required>
  </p>
  <button type="submit">Gönder</button>
</form>

Üç kural her şeyin temelidir. name olmayan alan sunucuya gönderilmez. id ile <label for> eşleşmesi alanın erişilebilir adını verir ve etikete tıklayınca alana odaklanılmasını sağlar. method="post", verinin adres satırında görünmemesi ve değişiklik yapan istekler içindir; arama gibi paylaşılabilir sorgularda get uygundur.

HTML5 hangi input tiplerini getirdi?

Tip Ne için Not
email E-posta adresi Biçim doğrulaması yapar; multiple ile virgüllü liste kabul eder.
url Web adresi Şema (https://) içermeyen değeri geçersiz sayar.
tel Telefon Biçim doğrulaması yapmaz; yalnızca telefon klavyesi açar.
number Adet, miktar Telefon, posta kodu ya da kart numarası için uygun değildir.
range Kesinliği önemsiz aralık Kaydırıcı; seçilen değeri ayrıca göstermek gerekir.
date, time, datetime-local Tarih ve saat Değer her zaman 2026-09-25, 14:30 gibi standart biçimdedir.
month, week Ay, hafta Firefox ve Safari masaüstünde desteklenmez, düz metne geri düşer.
color Renk seçici Değer #rrggbb biçimindedir.
search Arama kutusu Bazı tarayıcılarda temizleme düğmesi ekler.

Eski notlarımdaki datetime tipi standarttan çıkarıldı; yerine datetime-local kullanılır. Tarayıcının tanımadığı her tip hata vermeden text gibi davranır, bu yüzden month ve week kullanıyorsanız sunucu tarafında biçimi mutlaka denetleyin.

<label>Renk <input type="color" name="renk" value="#0b5fff"></label>
<label>Tarih <input type="date" name="tarih" min="2026-01-01" max="2026-12-31"></label>
<label>Saat <input type="time" name="saat" step="900"></label>
<label>Randevu <input type="datetime-local" name="randevu"></label>
<label>Adet <input type="number" name="adet" min="1" max="5" value="3"></label>
<label>Puan <input type="range" name="puan" min="0" max="10" value="5"></label>
<label>Web adresi <input type="url" name="site" placeholder="https://ornek.com"></label>
<label>Telefon <input type="tel" name="telefon" autocomplete="tel"></label>
<label>Belgeler <input type="file" name="belgeler" accept=".pdf,image/*" multiple></label>

time alanındaki step="900" değeri saniye cinsindendir ve 15 dakikalık adımlar tanımlar. Rakamlardan oluşan ama sayı olmayan veriler (posta kodu, doğrulama kodu, kart numarası) için type="text" ile inputmode="numeric" kullanın: sayısal klavye açılır ama baştaki sıfır silinmez, fare tekerleği değeri değiştirmez.

Form dışındaki alanlar ve gönder düğmesi öznitelikleri

form özniteliği, <form> etiketinin dışındaki bir alanı forma bağlar. Eski notumda birden fazla form kimliğinin virgülle yazılabileceği söyleniyordu; bu yanlış, öznitelik tek bir form id’si alır.

Gönder düğmeleri ise formun ayarlarını yalnızca kendileri için geçersiz kılabilir:

<form id="profil" action="/profil/kaydet" method="post">
  <label for="ad2">Ad</label>
  <input id="ad2" name="ad" required>

  <button type="submit">Kaydet</button>
  <button type="submit" formaction="/profil/taslak" formnovalidate>Taslak olarak kaydet</button>
  <button type="submit" formaction="/profil/onizle" formmethod="get" formtarget="_blank">Önizle</button>
</form>

<!-- Formun dışında ama form="profil" sayesinde onunla gönderilir -->
<label for="soyad2">Soyad</label>
<input id="soyad2" name="soyad" form="profil">
  • formaction: Gönderim adresini değiştirir.
  • formmethod: get, post ya da dialog.
  • formenctype: Kodlama türü; dosya yüklemede multipart/form-data olmalıdır ve yalnızca post ile anlam taşır.
  • formnovalidate: Bu düğmeyle gönderimde doğrulamayı atlar; taslak kaydetme için idealdir. Formun tamamında kapatmak için <form novalidate> kullanılır.
  • formtarget: Yanıtın açılacağı pencere; yeni sekme için _blank.

type="image" bir görseli gönder düğmesi olarak kullanır; alt, width ve height ile birlikte yazılmalıdır.

datalist, output ve kaldırılan keygen

<datalist>, bir metin alanına öneri listesi ekler. <select>’ten farkı, kullanıcının listede olmayan bir değer de yazabilmesidir; yani geçerli değerleri sunucuda yine denetlemeniz gerekir.

<label for="sehir">Şehir</label>
<input id="sehir" name="sehir" list="sehirler">
<datalist id="sehirler">
  <option value="Ankara"></option>
  <option value="İstanbul"></option>
  <option value="İzmir"></option>
  <option value="Kocaeli"></option>
</datalist>

<output>, bir hesaplamanın sonucunu gösterir ve for özniteliğiyle girdilerine bağlanır. Satır içi oninput yerine olay dinleyici kullanmak daha temizdir:

<form id="hesap">
  <input type="number" id="a" name="a" value="50"> +
  <input type="number" id="b" name="b" value="51"> =
  <output name="sonuc" for="a b">101</output>
</form>
<script>
  const hesap = document.getElementById("hesap");
  hesap.addEventListener("input", () => {
    const { a, b, sonuc } = hesap.elements;
    sonuc.value = (a.valueAsNumber || 0) + (b.valueAsNumber || 0);
  });
</script>

<keygen> ise tarayıcıda anahtar çifti üretip açık anahtarı formla göndermek için tasarlanmıştı. Standarttan çıkarıldı; Chrome öğeyi tanınmayan öğeye dönüştürdü, Firefox 69 sürümünde tamamen kaldırdı. Bugün kimlik doğrulama için WebAuthn tabanlı passkey’ler, anahtar üretimi için Web Crypto API kullanılır.

Doğrulama öznitelikleri nasıl çalışır?

Öznitelik Uygulandığı yer Hata durumu (validity)
required Çoğu alan valueMissing
type (email, url) İlgili tipler typeMismatch
minlength, maxlength Metin alanları tooShort, tooLong
min, max Sayı, tarih, saat, range rangeUnderflow, rangeOverflow
step Sayı, tarih, saat, range stepMismatch
pattern Metin, tel, email, url, search, password patternMismatch
<form action="/kayit" method="post">
  <label for="kullanici">Kullanıcı adı</label>
  <input id="kullanici" name="kullanici" required minlength="3" maxlength="20"
         pattern="[a-z0-9_]+" autocomplete="username"
         aria-describedby="kullanici-ipucu">
  <p id="kullanici-ipucu">3–20 karakter; küçük harf, rakam ve alt çizgi.</p>

  <label for="gsm">Cep telefonu (başında 0 olmadan)</label>
  <input id="gsm" name="gsm" type="tel" pattern="5[0-9]{9}" autocomplete="tel-national">

  <label for="posta-kodu">Posta kodu</label>
  <input id="posta-kodu" name="posta_kodu" inputmode="numeric" pattern="[0-9]{5}"
         autocomplete="postal-code">

  <label for="tutar">Tutar (TL)</label>
  <input id="tutar" name="tutar" type="number" min="0" step="0.01">

  <button type="submit">Kaydol</button>
</form>

pattern hakkında bilinmesi gerekenler:

  • Desen tüm değeri eşlemelidir; başına ^ sonuna $ yazmanıza gerek yoktur.
  • MDN’e göre desen, 2023’ten beri JavaScript’in v bayrağıyla derleniyor. Bu yüzden köşeli parantez sınıfı içinde tire gibi karakterleri kaçış karakteriyle yazın: [a-z\-].
  • Ad soyad alanlarını desenle kısıtlamayın. Eski notumdaki [A-Za-z]{2,} deseni “Şükrü” ya da “Güneş” adlarını reddediyordu.

number alanında step varsayılan olarak 1’dir; ondalıklı değer kabul etmek için step="0.01" ya da step="any" verin. İstemci doğrulaması yalnızca kullanıcı deneyimi içindir; tarayıcıyı atlayan bir istek bu kuralların hiçbirine takılmaz, aynı kontrolleri sunucuda da yapın.

Constraint Validation API ile özel doğrulama

Tarayıcının doğrulama motoru JavaScript’ten de yönetilebilir (MDN: Constraint validation). Her alanın validity nesnesi yukarıdaki tablodaki durumları, validationMessage tarayıcının mesajını verir. checkValidity() sessizce denetler, reportValidity() hata balonunu gösterir, setCustomValidity("mesaj") özel bir kural ekler ve boş metinle çağrılınca hatayı temizler.

Aşağıdaki örnek parola tekrarını denetler, hataları alanın altında gösterir ve ilk hatalı alana odaklanır:

<form id="kayit" action="/kayit" method="post" novalidate>
  <label for="parola">Parola</label>
  <input id="parola" name="parola" type="password" autocomplete="new-password"
         required minlength="12" aria-describedby="parola-hata">
  <p id="parola-hata" aria-live="polite"></p>

  <label for="parola2">Parola (tekrar)</label>
  <input id="parola2" name="parola2" type="password" autocomplete="new-password"
         required aria-describedby="parola2-hata">
  <p id="parola2-hata" aria-live="polite"></p>

  <button type="submit">Hesap oluştur</button>
</form>
const form = document.getElementById("kayit");
const { parola, parola2 } = form.elements;

function eslesmeyiKontrolEt() {
  const hata = parola2.value && parola2.value !== parola.value;
  parola2.setCustomValidity(hata ? "Parolalar eşleşmiyor." : "");
}

function hataGoster(alan) {
  const kutu = document.getElementById(`${alan.id}-hata`);
  if (alan.validity.valid) {
    alan.removeAttribute("aria-invalid");
    kutu.textContent = "";
    return;
  }
  alan.setAttribute("aria-invalid", "true");
  if (alan.validity.valueMissing) {
    kutu.textContent = "Bu alan zorunludur.";
  } else if (alan.validity.tooShort) {
    kutu.textContent = `En az ${alan.minLength} karakter girin.`;
  } else {
    kutu.textContent = alan.validationMessage;
  }
}

parola.addEventListener("input", eslesmeyiKontrolEt);
parola2.addEventListener("input", eslesmeyiKontrolEt);
for (const alan of [parola, parola2]) {
  alan.addEventListener("blur", () => hataGoster(alan));
}

form.addEventListener("submit", (olay) => {
  eslesmeyiKontrolEt();
  if (!form.checkValidity()) {
    olay.preventDefault();
    [parola, parola2].forEach(hataGoster);
    form.querySelector("[aria-invalid='true']")?.focus();
  }
});

novalidate tarayıcının kendi hata balonlarını kapatır ama doğrulama motorunu kapatmaz; validity ve checkValidity() çalışmaya devam eder. Yalnızca CSS ile görsel geri bildirim istiyorsanız, kullanıcı alanla etkileşime girmeden hata göstermeyen :user-invalid sözde sınıfını kullanın (MDN’e göre Kasım 2023’ten beri tüm büyük tarayıcılarda çalışıyor):

input:user-invalid {
  border-color: #b00020;
  outline: 2px solid #b00020;
}

autocomplete ile otomatik doldurmayı doğru yönlendirmek

Eski notlarımda autocomplete yalnızca on ve off değerleriyle anlatılmıştı. Asıl güç, alanın amacını bildiren değerlerdedir:

Değer Ne için
name, given-name, family-name Ad soyad, ad, soyad
email, tel, tel-national E-posta, telefon, ülke kodsuz telefon
username Giriş kimliği
current-password Giriş formundaki mevcut parola
new-password Kayıt ve parola değiştirme; tarayıcı güçlü parola önerebilir
one-time-code SMS ya da e-postayla gelen tek kullanımlık kod
organization Şirket adı
street-address, postal-code, country-name Adres, posta kodu, ülke adı
bday Doğum tarihi
cc-name, cc-number, cc-exp, cc-csc Kart bilgileri
<label>Doğrulama kodu
  <input name="kod" inputmode="numeric" autocomplete="one-time-code" maxlength="6">
</label>
<label>Teslimat adresi
  <textarea name="teslimat_adres" autocomplete="shipping street-address"></textarea>
</label>
<label>Fatura posta kodu
  <input name="fatura_posta" autocomplete="billing postal-code">
</label>

shipping ve billing önekleri iki adresi ayırır; aynı sayfada iki kişinin bilgisi varsa section- ile başlayan bir grup adı eklenir. Passkey girişinde autocomplete="username webauthn" kullanılır. Modern tarayıcılar giriş formlarında autocomplete="off" değerini çoğunlukla yok sayar; kapatmaya çalışmak yerine doğru amaç değerini verin.

Bu değerler erişilebilirlik gereği de karşılar: WCAG 2.2’nin 1.3.5 “Girdi Amacını Belirleme” başarı ölçütü (AA), kullanıcıya ait bilgileri isteyen alanların amacının programatik olarak belirlenmesini ister.

Erişilebilir etiketleme için kurallar

  • Her alanın görünür bir <label>’ı olsun. placeholder etiket değildir: yazmaya başlayınca kaybolur, kontrastı çoğu zaman düşüktür.
  • İlgili seçenekleri <fieldset> ve <legend> ile gruplayın. Ekran okuyucu her seçenekte grubun sorusunu da okur.
  • İpucu ve hata metnini aria-describedby ile bağlayın; hatalı alana aria-invalid="true" verin.
  • Hatayı yalnızca renkle göstermeyin; metinle açıklayın (WCAG 1.4.1 ve 3.3.1).
  • Parola alanında yapıştırmayı engellemeyin. Parola yöneticilerinin çalışabilmesi WCAG 2.2’deki 3.3.8 “Erişilebilir Kimlik Doğrulama” ölçütüyle de uyumludur.
<fieldset>
  <legend>Teslimat yöntemi</legend>
  <label><input type="radio" name="teslimat" value="kargo" checked> Kargo</label>
  <label><input type="radio" name="teslimat" value="magaza"> Mağazadan teslim</label>
</fieldset>

Sayfa düzeyindeki yapı için HTML5 anlamsal etiketler rehberindeki <search> ve landmark bölümüne, onay pencereleri için aynı rehberdeki <dialog> örneğine bakabilirsiniz. Formdan yüklenen görselleri önizlemek için Canvas rehberindeki drawImage bölümüne, video ve ses dosyalarını sayfada oynatmak için HTML5 video ve audio rehberine bakabilirsiniz.

Kontrol listesi

  • Her alanın name değeri ve <label for> ile bağlı görünür etiketi var.
  • Tipler amaca göre seçildi: telefon tel, rakamlı kodlar text + inputmode="numeric", tarih date.
  • Doğrulama kuralları hem HTML özniteliklerinde hem sunucuda aynı.
  • Kişisel veri alanlarında doğru autocomplete değerleri var; giriş ve kayıt formlarında current-password / new-password ayrımı yapıldı.
  • Hata mesajları metin olarak, alana aria-describedby ile bağlı; gönderimde ilk hatalı alana odaklanılıyor.

Sık sorulan sorular

HTML5 form doğrulaması yeterli mi?

Hayır. Tarayıcı doğrulaması kullanıcıya hızlı geri bildirim vermek içindir; geliştirici araçlarıyla ya da doğrudan HTTP isteğiyle kolayca atlanabilir. Aynı kuralları sunucu tarafında mutlaka yeniden uygulayın.

autocomplete off neden çalışmıyor?

Modern tarayıcılar, parola yöneticisinin giriş formlarını doldurmasını kullanıcı yararına kabul eder ve bu alanlarda autocomplete off değerini çoğunlukla yok sayar. Otomatik doldurmayı kapatmak yerine alanın amacını doğru değerle (current-password, new-password, one-time-code) bildirmek daha iyi sonuç verir.

Telefon numarası için input type number mı kullanılmalı?

Hayır. number tipi ok tuşlarıyla artırma, baştaki sıfırı silme ve kaydırma tekerleğiyle değer değiştirme gibi sayısal davranışlar getirir. Telefon için type tel, posta kodu ve kart numarası gibi rakamlı kodlar için type text ile inputmode numeric kullanın.

keygen etiketi yerine ne kullanılır?

keygen standarttan kaldırıldı ve güncel tarayıcılarda çalışmıyor. Kullanıcı kimlik doğrulaması için WebAuthn tabanlı passkey'ler, tarayıcıda anahtar çifti üretmek için Web Crypto API'nin generateKey yöntemi kullanılır.

Kaynaklar

  1. WHATWG HTML Standard — Forms html.spec.whatwg.org
  2. WHATWG HTML Standard — Autofill (autocomplete değerleri) html.spec.whatwg.org
  3. MDN — Constraint validation developer.mozilla.org
  4. MDN — HTML autocomplete attribute developer.mozilla.org
  5. MDN — HTML pattern attribute developer.mozilla.org
  6. W3C — Understanding WCAG 2.2 SC 1.3.5 Identify Input Purpose w3.org

HTML5HTML FormForm DoğrulamaConstraint Validation APIautocompleteErişilebilirlikMarkdown sürümü

İletişim

Konuşalım.