Mert Özen Her Satırda Daha İleri
Frontend JavaScript Temel — #16

JavaScript ile Form İşlemleri: Değer Okuma, Submit ve Basit Doğrulama

Mert Özen 3 Kas 2026 9 dk 5 görüntülenme
JavaScript ile Form İşlemleri: Değer Okuma, Submit ve Basit Doğrulama

Form alanlarından value, checked ve selectedIndex ile değer okuyor, submit event'ini yakalıyor, boş alan, e-posta ve uzunluk kontrolü yazıp hataları sayfada gösteriyor, HTML'in yerleşik doğrulamasını tanıyoruz.

Sürüm notu: Bu yazıdaki kodlar ES2026 ve güncel tarayıcılar (Chrome, Edge, Firefox, Safari) içindir.

Önce Tahmin Et

Sayfada, içinde 30 yazan bir sayı kutusu var. Kutunun tipi açıkça number:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Predict</title>
</head>
<body>
  <input id="age" type="number" value="30">
  <script>
    const age = document.getElementById("age").value;
    console.log(age + 1);
  </script>
</body>
</html>

Konsola ne yazılır? 31 mi, başka bir şey mi? Okumaya devam etmeden önce bir tahmin yap. Cevap, form alanlarının değerlerini nasıl okuduğumuzu konuştuğumuz bölümde.

Formun Parçaları ve Onlara Ulaşmak

Formu bir dilekçe gibi düşün. Kutucuklar boş gelir, kullanıcı doldurur, sen de teslim alırken eksik ya da hatalı bir şey var mı diye bakarsın. JavaScript'in formdaki işi de tam bu: değerleri okumak, gönderimi yakalamak ve gerekiyorsa "şurayı düzelt" demek.

Bütün yazı boyunca kullanacağımız küçük bir kayıt formuyla başlayalım. Kaydet, aç, alanları doldur ve Sign up butonuna bas:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Sign-up Form</title>
</head>
<body>
  <form id="signup">
    <p>
      <label for="name">Name</label>
      <input id="name" type="text">
    </p>
    <p>
      <label for="age">Age</label>
      <input id="age" type="number">
    </p>
    <p>
      <label for="city">City</label>
      <select id="city">
        <option value="">Choose one</option>
        <option value="ist">Istanbul</option>
        <option value="ank">Ankara</option>
        <option value="izm">Izmir</option>
      </select>
    </p>
    <p>
      <label><input type="radio" name="plan" value="free" checked> Free</label>
      <label><input type="radio" name="plan" value="pro"> Pro</label>
    </p>
    <p>
      <label><input id="newsletter" type="checkbox"> Subscribe to the newsletter</label>
    </p>
    <button>Sign up</button>
  </form>
  <script>
    const form = document.getElementById("signup");

    form.addEventListener("submit", (event) => {
      event.preventDefault();

      const name = document.getElementById("name").value.trim();
      const ageText = document.getElementById("age").value;
      const cityBox = document.getElementById("city");
      const plan = document.querySelector('input[name="plan"]:checked').value;
      const newsletter = document.getElementById("newsletter").checked;

      console.log("Name:", name);
      console.log("Age:", Number(ageText));
      console.log("City code:", cityBox.value);
      console.log("Selected index:", cityBox.selectedIndex);
      console.log("Visible text:", cityBox.options[cityBox.selectedIndex].text);
      console.log("Plan:", plan);
      console.log("Newsletter:", newsletter);
    });
  </script>
</body>
</html>

Form alanları da birer DOM element'i; daha önce gördüğümüz getElementById ve querySelector ile onlara aynen ulaşırsın. <label>'ın for attribute'u, label'ı aynı id'ye sahip kutuya bağlar. Kullanıcı yazıya tıkladığında kutu seçilir, ekran okuyucular da kutunun ne işe yaradığını okuyabilir. Küçük bir ayrıntı gibi dursa da formun kullanılabilirliğini ciddi biçimde artırır.

Form alanlarına toplu ulaşmanın daha pratik yolları da var; form.elements ve FormData gibi. Onları Orta seride detaylandıracağız. Şimdilik her alanı tek tek seçmek, neyin nereden geldiğini görmek için daha öğretici.

value, checked ve selectedIndex

Metin kutularının, sayı kutularının ve select'lerin içeriği value property'sindedir. Baştaki sorunun cevabı da burada saklı: konsolda 31 değil 301 görürsün. value her zaman bir string döndürür. Kutunun tipinin number olması bunu değiştirmez; tarayıcı sadece kullanıcının harf girmesini zorlaştırır. Elindeki "30" bir string olduğu için + 1 toplama değil, daha önce gördüğümüz string birleştirme yapar. Sayıyla hesap yapacaksan önce Number() ile çevir.

Checkbox ve radio butonlarda asıl soru "işaretli mi?" olduğu için checked property'sine bakarsın; true ya da false döner. Radio grubunda, aynı name'e sahip butonlardan hangisinin seçili olduğunu bulmak için :checked selector'ı işini görür:

const selected = document.querySelector('input[name="plan"]:checked');
console.log(selected ? selected.value : "Nothing selected");

Hiçbiri seçili değilse querySelector null döner, o yüzden önce kontrol etmek iyi bir alışkanlık. Bizim formda bir seçeneği baştan checked yaptığımız için bu sorun yaşanmıyor.

Select'te value, seçili <option>'ın value attribute'unu verir: ekranda "Istanbul" görünür ama sen "ist" okursun. selectedIndex seçili seçeneğin sırasını söyler, sıfırdan başlayarak. Hiçbir seçenek seçili değilse -1 döner. Kullanıcının gördüğü metni istiyorsan options[selectedIndex].text ile alırsın.

submit Event'i: Gönderimi Yakalamak

Değerleri ne zaman okuyacağın, nasıl okuyacağın kadar önemli. Yukarıdaki örnekte bütün okumaları submit listener'ının içinde yaptık. Sebebi basit: kullanıcı formu doldurduktan sonra, gönder dediği anda kutuların içinde ne olduğunu öğrenmek istiyoruz.

Daha önce event'ler yazısında gördüğümüz iki kural burada da geçerli. Birincisi, listener'ı butona değil forma ekle. Kullanıcı Enter'a basarak da formu gönderebilir; submit her iki durumda da çalışır, butonun click'i ise sadece tıklamada. İkincisi, event.preventDefault()'u unutma. Tarayıcının varsayılan davranışı formu bir adrese gönderip sayfayı yenilemektir. Biz veriyi JavaScript ile işleyeceğimiz için bunu durduruyoruz. Formu sayfayı yenilemeden bir sunucuya göndermeyi, Fetch API'yi anlattığımızda göreceğiz.

Basit Doğrulama Kuralları

Doğrulama, "bu veriyi kabul edebilir miyim?" sorusuna verilen cevaptır. Günlük hayatta en sık karşılaşacağın üç kontrol şunlar: alan boş mu, e-posta e-postaya benziyor mu, uzunluk yeterli mi. Her birini, sorun varsa bir hata mesajı, yoksa boş string döndüren küçük bir fonksiyon olarak yazmak işleri çok kolaylaştırır:

function checkName(name) {
  if (name === "") return "Name can't be empty.";
  if (name.length < 2) return "Name must be at least 2 characters.";
  return "";
}

function checkEmail(email) {
  if (email === "") return "Email can't be empty.";
  if (!email.includes("@") || !email.includes(".")) {
    return "Enter a valid email address.";
  }
  return "";
}

function checkPassword(password) {
  if (password.length < 8) return "Password must be at least 8 characters.";
  return "";
}

console.log(checkName("  ".trim())); // "Name can't be empty."
console.log(checkEmail("ada@example.com")); // ""
console.log(checkPassword("1234")); // "Password must be at least 8 characters."

Bunu konsola yapıştırıp kendi değerlerinle dene. Boşluk kontrolünden önce trim() kullandığımıza dikkat et; yoksa sadece boşluk tuşuna basan biri "dolu" bir alanla geçip gider. E-posta kontrolü bilerek kaba tutuldu: içinde @ ve nokta var mı, ona bakıyor. Daha sıkı bir kontrolü düzenli ifadelerle yazacağız; o konu da çok yakında.

Hataları Sayfada Göstermek

Doğrulamanın yarısı kontrol, yarısı da kullanıcıya ne yanlış gittiğini açıkça söylemek. Hata mesajını konsola yazmak sana yeter ama kullanıcı konsolu görmez. Mesajı ilgili alanın hemen yanına yazmak en anlaşılır yoldur. Bu dosya her şeyi bir araya getiriyor:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Validation</title>
  <style>
    .error { color: firebrick; font-size: 14px; }
    .invalid { border: 2px solid firebrick; }
  </style>
</head>
<body>
  <form id="signup" novalidate>
    <p>
      <label for="name">Name</label>
      <input id="name">
      <span class="error" id="name-error"></span>
    </p>
    <p>
      <label for="email">Email</label>
      <input id="email" type="email">
      <span class="error" id="email-error"></span>
    </p>
    <p>
      <label for="password">Password</label>
      <input id="password" type="password">
      <span class="error" id="password-error"></span>
    </p>
    <p>
      <label><input id="terms" type="checkbox"> I accept the terms of use</label>
      <span class="error" id="terms-error"></span>
    </p>
    <button>Sign up</button>
  </form>
  <p id="result"></p>
  <script>
    const form = document.getElementById("signup");
    const result = document.getElementById("result");

    function checkName(name) {
      if (name === "") return "Name can't be empty.";
      if (name.length < 2) return "Name must be at least 2 characters.";
      return "";
    }

    function checkEmail(email) {
      if (email === "") return "Email can't be empty.";
      if (!email.includes("@") || !email.includes(".")) {
        return "Enter a valid email address.";
      }
      return "";
    }

    function checkPassword(password) {
      if (password.length < 8) return "Password must be at least 8 characters.";
      return "";
    }

    function showError(fieldId, message) {
      document.getElementById(fieldId + "-error").textContent = message;
      document.getElementById(fieldId).classList.toggle("invalid", message !== "");
    }

    form.addEventListener("submit", (event) => {
      event.preventDefault();

      const nameError = checkName(document.getElementById("name").value.trim());
      const emailError = checkEmail(document.getElementById("email").value.trim());
      const passwordError = checkPassword(document.getElementById("password").value);
      const termsError = document.getElementById("terms").checked
        ? ""
        : "You need to accept the terms to continue.";

      showError("name", nameError);
      showError("email", emailError);
      showError("password", passwordError);
      showError("terms", termsError);

      if (nameError || emailError || passwordError || termsError) {
        result.textContent = "";
        return;
      }

      result.textContent = "Signed up successfully, welcome!";
      form.reset();
    });
  </script>
</body>
</html>

Her alanın yanında boş bir <span> duruyor. showError fonksiyonu mesajı bu span'a textContent ile yazıyor ve kutuya kırmızı çerçeve veren class'ı ekleyip çıkarıyor. classList.toggle'a verdiğimiz ikinci parametre, class'ın eklenip eklenmeyeceğini doğrudan söyler: true ise ekler, false ise kaldırır. Böylece kullanıcı hatasını düzeltip tekrar gönderdiğinde eski mesaj da kendiliğinden temizlenir.

if (nameError || emailError || ...) satırı daha önce gördüğümüz truthy/falsy mantığına dayanıyor: boş string falsy, dolu bir mesaj truthy. Herhangi bir alanda mesaj varsa erken return ile çıkıyoruz. Hepsi temizse başarı mesajını yazıp form.reset() ile alanları sıfırlıyoruz. Form etiketindeki novalidate'in ne işe yaradığını hemen şimdi göreceğiz.

HTML'in Yerleşik Doğrulaması

Bazı kontroller için tek satır JavaScript yazmana bile gerek yok. HTML'in kendisi bir grup doğrulama attribute'u sunar:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Built-in Validation</title>
</head>
<body>
  <form id="signup">
    <p><input id="name" placeholder="Name" required minlength="2"></p>
    <p><input id="email" type="email" placeholder="Email" required></p>
    <p><input id="password" type="password" placeholder="Password" required minlength="8"></p>
    <p><input id="age" type="number" placeholder="Age" min="18" max="120"></p>
    <button>Sign up</button>
  </form>
  <script>
    document.getElementById("signup").addEventListener("submit", (event) => {
      event.preventDefault();
      console.log("submit ran, every field is valid.");
    });
  </script>
</body>
</html>

required alanı zorunlu yapar. minlength ve maxlength metnin uzunluğunu, min ve max sayının aralığını sınırlar. type="email" değerin e-posta biçiminde olmasını ister. Formu boş göndermeyi dene: tarayıcı ilk hatalı alanın yanında kendi uyarı balonunu gösterir ve konsola hiçbir şey yazılmaz. Çünkü tarayıcı bu kontrolleri submit event'inden önce yapar; form geçersizse event hiç tetiklenmez.

Bu attribute'lar bedava bir ilk savunma hattıdır ve mutlaka kullanmalısın. Eksik yanları da var: mesajlar tarayıcının dilinde ve tarzında çıkar, görünümünü pek değiştiremezsin. Bir önceki örnekte form etiketine novalidate eklememizin sebebi buydu. O attribute tarayıcının kendi uyarılarını kapatır ve kontrolü tamamen bizim JavaScript koduna bırakır. Bir de pattern attribute'u var; bir alanın belirli bir kalıba uymasını ister ve düzenli ifadelerle çalışır. HTML doğrulamasını JavaScript'ten okuyup kendi mesajlarınla birleştirmenin yolunu Orta seride göreceğiz.

Sık Düşülen Tuzaklar

Sayıyı string olarak kullanmak

Baştaki soruda gördüğümüz gibi value her zaman string. Bir inceliği daha var: boş bir sayı kutusunda Number("") 0 verir, NaN değil. Yani "kullanıcı yaş girmedi" ile "kullanıcı 0 yazdı" aynı görünür. Boşluk kontrolünü sayıya çevirmeden önce yap.

Değeri yanlış zamanda okumak

const name = document.getElementById("name").value; satırını listener'ın dışına, script'in en başına yazarsan değer sayfa açıldığı anda okunur. O anda kutu boştur ve değişkenin içinde sonsuza dek boş string kalır. Değeri, ihtiyaç duyduğun anda, yani listener'ın içinde oku.

Form içindeki her butonun gönderme yapması

Form içindeki bir <button>'ın varsayılan tipi submit'tir. Forma "Clear" ya da "Show password" gibi ikinci bir buton eklersen, ona tıklamak da formu gönderir. Göndermemesi gereken butonlara type="button" yaz.

preventDefault'u unutmak

Sayfa yenilendiği için konsoldaki mesajlar da bir anda kaybolur ve "kod hiç çalışmıyor" sanırsın. Böyle bir durumda DevTools'un konsol ayarlarındaki Preserve log (Firefox'ta Persist Logs) seçeneği, sayfa yenilense de mesajları korur.

Tarayıcıdaki doğrulamaya güvenmek

Tarayıcıdaki doğrulama kullanıcıyı yönlendirmek içindir, güvenlik için değil. Kodun kullanıcının bilgisayarında çalışıyor; isteyen DevTools'u açıp kontrollerini kapatabilir. Veriyi alan sunucu aynı kontrolleri mutlaka kendisi de yapmalı.

Küçük Bir Deneme

★☆☆ Doğrulama örneğindeki her kutuya bir input listener'ı ekle. Kullanıcı yazmaya başladığında o alanın hata mesajı silinsin.

★★☆ Çalıştırmadan önce tahmin et: kayıt formunda yaş kutusunu boş bırakıp Sign up butonuna basarsan konsolda "Age:" satırının yanında ne görürsün? Bir de yaş kutusuna 25 yazıp document.getElementById("age").value + 5 ifadesini konsolda dene.

★★★ Doğrulama örneğine "Repeat password" alanı ekle. İki şifre aynı değilse "Passwords don't match." mesajını göster. Bir de "Show password" checkbox'ı ekle; işaretlenince şifre kutusunun type'ını "text", kaldırılınca tekrar "password" yap.

Şu ana kadar hep sayfanın içiyle uğraştık: element'ler, event'ler, formlar. Bir sonraki yazıda bir adım geri çekilip sayfayı taşıyan pencereye bakacağız. Adres çubuğunu okumayı, tarayıcı geçmişinde gezinmeyi, ekran boyutunu öğrenmeyi ve zamanlayıcılarla kodu ileri bir ana ertelemeyi göreceğiz.