Episode ini membahas form dari dua sisi: validasi HTML5 bawaan yang cepat, dan validasi JavaScript custom untuk aturan yang lebih kompleks. Kalian juga belajar menampilkan pesan error yang jelas, validasi saat mengetik, dan memproses submit secara asynchronous.

Form adalah gerbang utama aplikasi web menerima data dari pengguna — dan sekaligus sumber paling banyak data kotor. Pengguna bisa mengirim string kosong, email tanpa @, atau angka di tempat teks. Validasi memastikan data yang masuk sesuai aturan, baik sebelum sampai ke server maupun sebelum diproses.
Episode 17 membahas validasi berlapis. Lapisan pertama adalah validasi HTML5 yang bawaan browser — cepat dan tanpa kode JavaScript. Lapisan kedua adalah validasi JavaScript custom untuk aturan yang tidak bisa diungkapkan HTML. Terakhir, kalian membangun feedback yang baik: pesan error yang jelas, validasi saat mengetik, dan submit yang diproses secara asynchronous.
Prinsip yang harus dipegang: validasi client-side untuk pengalaman pengguna, validasi server-side untuk keamanan. Keduanya wajib — yang satu tidak menggantikan yang lain.
Mulai dari struktur HTML yang benar. Elemen kunci: form, label yang terhubung dengan input, dan tipe input yang sesuai:
<form id="form-daftar" novalidate>
<label for="nama">Nama</label>
<input type="text" id="nama" name="nama" required minlength="3" />
<label for="email">Email</label>
<input type="email" id="email" name="email" required />
<button type="submit">Daftar</button>
<p id="pesan-error" hidden></p>
</form><input type="email" memberitahu browser bahwa kolom ini harus berformat email. required menandai kolom wajib, minlength="3" membatasi panjang minimum. Atribut novalidate pada form sengaja dimatikan — supaya kalian bisa menerapkan validasi JavaScript dengan kontrol penuh. Elemen p dengan id pesan-error akan menampung feedback.
Bahkan tanpa novalidate, browser punya validasi HTML5 bawaan. Saat validasi gagal, event invalid dan property validity memberi informasi detail:
const inputEmail = document.querySelector("#email");
inputEmail.addEventListener("input", () => {
console.log("valid:", inputEmail.validity.valid);
console.log("typeMismatch:", inputEmail.validity.typeMismatch);
console.log("valueMissing:", inputEmail.validity.valueMissing);
});inputEmail.validity.valid bernilai true saat input lolos semua aturan HTML5. validity.typeMismatch menandai format salah, validity.valueMissing menandai kolom wajib yang kosong. Saat form mencoba submit dan ada yang gagal, event invalid terpicu dan validationMessage berisi pesan bawaan browser — yang bisa kalian tampilkan sendiri tanpa popup bawaan.
Aturan seperti "password harus mengandung angka" atau "konfirmasi harus sama dengan password" tidak bisa diungkapkan dengan atribut HTML. Ini pekerjaan JavaScript:
function validasiPassword(kataSandi, konfirmasi) {
if (kataSandi.length < 8) {
return "Password minimal 8 karakter";
}
if (!/[0-9]/.test(kataSandi)) {
return "Password harus mengandung angka";
}
if (kataSandi !== konfirmasi) {
return "Konfirmasi password tidak cocok";
}
return null;
}
console.log(validasiPassword("rahasia", "rahasia"));
console.log(validasiPassword("rahasia12", "rahasia12"));validasiPassword mengembalikan string pesan error, atau null jika valid. Memisahkan aturan ke fungsi murni seperti ini memudahkan pengujian. null sebagai penanda "valid" adalah konvensi yang jelas dan mudah diperiksa.
Pesan error terbaik ditampilkan dekat input yang bermasalah, bukan menumpuk di satu tempat:
function tampilkanError(input, pesan) {
const errorEl = input.nextElementSibling;
if (pesan === null) {
errorEl.hidden = true;
input.classList.remove("salah");
} else {
errorEl.textContent = pesan;
errorEl.hidden = false;
input.classList.add("salah");
}
}
const inputNama = document.querySelector("#nama");
tampilkanError(inputNama, validasiNama(inputNama.value));tampilkanError memakai input.nextElementSibling — asumsi elemen error menempel setelah input di HTML. Saat valid, pesan disembunyikan dan class salah dihapus; saat tidak, pesan ditampilkan dan input ditandai merah. Konsistensi posisi error ini membuat pengguna tidak perlu mencari-cari.
Validasi yang hanya terjadi saat submit terasa lamban. Pola umum: validasi ringan saat input berlangsung, validasi penuh saat blur dan submit:
const inputNama = document.querySelector("#nama");
const form = document.querySelector("#form-daftar");
inputNama.addEventListener("input", () => {
const pesan = inputNama.value.length < 3 ? "Nama minimal 3 karakter" : null;
tampilkanError(inputNama, pesan);
});
form.addEventListener("submit", (event) => {
event.preventDefault();
const pesan = validasiNama(inputNama.value);
tampilkanError(inputNama, pesan);
if (pesan === null) {
console.log("Form valid, siap dikirim");
}
});inputNama.addEventListener("input", ...) memperbarui pesan di setiap ketikan. Saat submit, event.preventDefault() menghentikan pengiriman default, validasi terakhir dijalankan, dan hanya jika bersih data diproses. Pengguna mendapat umpan balik seketika tanpa menunggu submit.
Jika semua kolom valid, kirim data ke server dengan fetch dan tampilkan hasilnya:
async function kirimPendaftaran(data) {
const respons = await fetch("/api/daftar", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data),
});
if (!respons.ok) {
throw new Error(`HTTP ${respons.status}`);
}
return respons.json();
}
const dataForm = {
nama: document.querySelector("#nama").value,
email: document.querySelector("#email").value,
};
await kirimPendaftaran(dataForm);
console.log("Pendaftaran berhasil");async function kirimPendaftaran(data) memakai pola fetch POST dari episode 15 lengkap dengan pengecekan respons.ok. Memisahkan fungsi pengiriman dari logika form membuat kode mudah diuji, dan error await ditangkap dengan try...catch seperti di episode 16.
Episode 17 membuat kalian membangun form yang andal: validasi HTML5 dengan Constraint Validation API, validasi custom untuk aturan kompleks, tampilan pesan error yang jelas dekat input, validasi saat mengetik, dan submit asynchronous dengan fetch.
Inti yang harus dibawa pulang:
validity dan validationMessage memberi akses programatik ke validasi browser.novalidate memberi kontrol penuh pada validasi JavaScript.Di episode 18 selanjutnya kita akan membahas Web Storage, cookies, dan state client-side — menyimpan data di browser dengan localStorage dan sessionStorage, bekerja dengan cookies, serta pola state yang aman dan efektif. Kalian akan tahu kapan harus memakai yang mana.