Pengunjung toko buku Budi jarang mengisi form dengan sempurna. Ada yang menekan tombol daftar saat kolom masih kosong, ada yang menulis email tanpa tanda @, dan ada pula yang mengetik satu huruf lalu berharap itu sudah dianggap nama. Bila data yang belum diperiksa langsung dikirim, yang menerima pun bingung — dan pengguna tidak tahu apa yang perlu dibetulkan.

Validasi adalah langkah pemeriksaan data sebelum data itu dianggap sah. Di chapter ini kita menulis validasi manual dengan JavaScript: aturan dinyatakan sebagai function, hasil pemeriksaan ditampilkan sebagai pesan galat inline di dekat kolomnya, dan urutan cek dijaga tetap sehingga keluarannya terprediksi. Browser sebenarnya punya mesin validasi bawaan — itu bahasan chapter berikutnya — tetapi memahami versi manualnya membuat kita tahu persis apa yang sedang otomatis dijalankan browser.

Aturan yang Kita Tulis Sendiri #

Form pendaftaran versi kita punya dua kolom dan dua aturan sederhana:

  • Nama: wajib diisi, minimal 3 karakter.
  • Email: wajib diisi, mengikuti bentuk dasar nama@domain dengan titik di domain.

Aturan di atas dinyatakan sebagai function yang menerima nilai dan mengembalikan hasil pemeriksaan. Kontraknya dibuat seragam: function mengembalikan string pesan galat bila gagal, atau string kosong bila lolos. Dengan kontrak ini, pemanggil cukup memeriksa pesan === "" untuk mengetahui hasilnya — tidak ada null, undefined, atau true yang bentuknya berbeda-beda.

Ringkasannya bisa disusun sebagai tabel keputusan yang mudah diperiksa ulang:

KolomKondisiPesan yang ditampilkan
namakosong setelah trim“Nama wajib diisi.”
namakurang dari 3 karakter“Nama minimal 3 karakter.”
emailkosong setelah trim“Email wajib diisi.”
emailtidak cocok pola dasar“Format email belum benar.”

Tabel ini sekaligus menjadi kontrak pesan: teks yang tampil untuk pengguna ditetapkan di satu tempat, dan function cek tidak pernah menyusun kalimat sendiri di tempat lain.

html
<form id="form-daftar">
  <label for="nama">Nama Lengkap</label>
  <input id="nama" name="nama" type="text">
  <p id="galat-nama" hidden></p>

  <label for="email">Email</label>
  <input id="email" name="email" type="text">
  <p id="galat-email" hidden></p>

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

Perhatikan elemen <p id="galat-nama" hidden> di bawah tiap kolom. Elemen itulah wadah pesan galat inline — tersembunyi sejak awal, baru muncul saat ada yang perlu disampaikan.

Fungsi Cek dan Pesan Galat Inline #

// validasi-pendaftaran.js — validasi manual dengan pesan galat inline
const form = document.querySelector("#form-daftar");
const nama = form.querySelector("#nama");
const email = form.querySelector("#email");

// setiap fungsi cek mengembalikan pesan galat, atau string kosong bila lolos
function cekNama(nilai) {
  if (nilai.length === 0) return "Nama wajib diisi.";
  if (nilai.length < 3) return "Nama minimal 3 karakter.";
  return "";
}

function cekEmail(nilai) {
  if (nilai.length === 0) return "Email wajib diisi.";
  if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(nilai)) return "Format email belum benar.";
  return "";
}

// tampilkan pesan pada <p> tepat di bawah kolomnya
function tampilkanGalat(idPesan, teks) {
  const kotakPesan = form.querySelector("#" + idPesan);
  kotakPesan.textContent = teks;
  kotakPesan.hidden = teks === "";
}

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

  // urutan cek tetap: nama lebih dulu, email menyusul
  const galatNama = cekNama(nama.value.trim());
  const galatEmail = cekEmail(email.value.trim());

  tampilkanGalat("galat-nama", galatNama);
  tampilkanGalat("galat-email", galatEmail);

  const daftarGalat = [galatNama, galatEmail].filter(function (pesan) {
    return pesan !== "";
  });
  if (daftarGalat.length === 0) {
    console.log("Valid: pendaftaran atas nama", nama.value.trim());
  } else {
    for (const pesan of daftarGalat) {
      console.log("Galat:", pesan);
    }
  }
});

Kita jalankan tiga skenario berurutan pada form yang sama: pertama submit dengan kedua kolom kosong, kedua dengan nama Budi dan email budi@salah (tanpa titik di domain), ketiga dengan data yang benar.

Output yang Dihasilkan (console browser):

text
Galat: Nama wajib diisi.
Galat: Email wajib diisi.
Galat: Format email belum benar.
Valid: pendaftaran atas nama Budi Santoso

Mari kita bedah bagian-bagian pentingnya:

  1. Baris 2–4 (pencarian elemen): form, kedua kolom, dan dua wadah pesan dirujuk lewat id. Handler selalu membaca nama.value saat dieksekusi, jadi isinya selalu yang terbaru.
  2. Baris 7–17 (fungsi cek): setiap function memuat aturan berurutan dan berhenti pada pelanggaran pertama lewat return dini. Pola regex pada cekEmail membaca bentuk dasar email: ^ dan $ memakai kecocokan dari ujung ke ujung, [^\s@]+ berarti satu rangkaian karakter yang bukan spasi atau @, dan bagian setelah @ wajib memuat titik. Pola lengkapnya sudah kita bahas pada section Strings dan Regex.
  3. Baris 20–24 (tampilkanGalat): pesan ditulis dengan textContent — bukan innerHTML — sehingga isi pesan selalu diperlakukan sebagai teks. Properti hidden menyembunyikan wadah ketika pesannya string kosong, dan memunculkannya saat ada pesan.
  4. Baris 27 (event.preventDefault()): pengiriman bawaan yang memuat ulang halaman dihentikan lebih dulu, agar pemeriksaan sempat berjalan dan halaman tidak kehilangan isian.
  5. Baris 30–34 (urutan cek): cekNama dipanggil sebelum cekEmail, dan pesan ditampilkan dalam urutan yang sama dengan tata letak kolom. Fungsi trim() memangkas spasi di ujung isian sebelum diperiksa — spasi tersembunyi sering lolos dari mata pengguna.
  6. Baris 36–45 (keputusan akhir): kedua hasil dikelompokkan lewat filter, lalu bila kosong, pendaftaran dinyatakan valid. Keluaran skenario pertama memperlihatkan dua pesan tampil sekaligus, skenario kedua hanya satu — urutan tidak pernah berubah.

Urutan Cek: Tetap dan Terprediksi #

Urutan cek bukan detail kosmetik. Bila cekEmail tiba-tiba berjalan sebelum cekNama, pengguna yang kedua kolomnya salah akan membaca pesan tentang email lebih dulu — sesuatu yang tidak ada hubungannya dengan kolom paling atas. Menjaga urutan cek searah dengan tata letak form membuat pesan selalu menyambut pengguna di tempat yang wajar.

Urutan juga berlaku di dalam satu function: aturan yang paling mungkin terjadi — kolom kosong — diperiksa sebelum aturan yang lebih khusus. Pada keluaran skenario pertama, pesan “Nama wajib diisi.” muncul, bukan “Nama minimal 3 karakter.”, meskipun string kosong juga gagal aturan panjang: return dini membuat pemeriksaan berhenti di pelanggaran pertama.

Kapan Validasi Dijalankan #

Tiga momen yang lazim: pada input untuk reaksi langsung (penghitung karakter, pratinjau), pada change untuk pemeriksaan per kolom, dan pada submit untuk pemeriksaan menyeluruh sebelum data dikirim. Form pada chapter ini hanya memasang listener submit, dan pilihan itu sengaja: satu titik pemeriksaan lebih mudah dipelajari dulu, sementara variasi lain tinggal ditambahkan dengan pola yang sudah kita pegang.

Form kita juga tidak membawa atribut validasi apa pun, sehingga browser tidak punya aturan untuk dipaksakan sendiri — semua keputusan ada di tangan function kita. Begitu atribut seperti required ikut bermain, mesin validasi bawaan browser ikut bicara. Mesin itulah topik chapter berikutnya.

Satu catatan penutup tentang trim() di baris 30–31: pemangkasan itu bukan sekadar kerapian. Isian yang hanya berisi spasi akan memotong dirinya menjadi string kosong, sehingga cek nilai.length === 0 menolaknya dengan pesan “wajib diisi” — tanpa trim, isian semacam itu lolos sebagai “nama” sepanjang beberapa karakter spasi. Pemeriksaan panjang pun lebih adil setelah trim, karena spasi di ujung tidak lagi dihitung sebagai isi.

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

validasi-pendaftaran.js

Unduh berkas
javascript
// validasi-pendaftaran.js — validasi manual dengan pesan galat inline
const form = document.querySelector("#form-daftar");
const nama = form.querySelector("#nama");
const email = form.querySelector("#email");

// setiap fungsi cek mengembalikan pesan galat, atau string kosong bila lolos
function cekNama(nilai) {
  if (nilai.length === 0) return "Nama wajib diisi.";
  if (nilai.length < 3) return "Nama minimal 3 karakter.";
  return "";
}

function cekEmail(nilai) {
  if (nilai.length === 0) return "Email wajib diisi.";
  if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(nilai)) return "Format email belum benar.";
  return "";
}

// tampilkan pesan pada <p> tepat di bawah kolomnya
function tampilkanGalat(idPesan, teks) {
  const kotakPesan = form.querySelector("#" + idPesan);
  kotakPesan.textContent = teks;
  kotakPesan.hidden = teks === "";
}

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

  // urutan cek tetap: nama lebih dulu, email menyusul
  const galatNama = cekNama(nama.value.trim());
  const galatEmail = cekEmail(email.value.trim());

  tampilkanGalat("galat-nama", galatNama);
  tampilkanGalat("galat-email", galatEmail);

  const daftarGalat = [galatNama, galatEmail].filter(function (pesan) {
    return pesan !== "";
  });
  if (daftarGalat.length === 0) {
    console.log("Valid: pendaftaran atas nama", nama.value.trim());
  } else {
    for (const pesan of daftarGalat) {
      console.log("Galat:", pesan);
    }
  }
});