Validasi manual pada chapter sebelumnya bekerja, tetapi ada pekerjaan yang sebenarnya sudah dikerjakan browser: aturan seperti “kolom ini wajib” atau “isi harus cocok pola ini” begitu umum sehingga HTML menyediakan atributnya, dan browser siap memeriksa setiap form dikirim. Membiarkan aturan dasar berpindah ke HTML mengurangi kode yang harus kita rawat.
Chapter ini membedah Constraint Validation API: atribut validasi HTML, object validity yang melaporkan detail kegagalan, method checkValidity() dan reportValidity(), serta setCustomValidity() untuk aturan yang hanya kita ketahui. Form yang kita pakai adalah form penukaran kupon di toko Budi — kolom kode kupon dengan pola tertentu dan kolom email.
Atribut Validasi dan Pemeriksaan Otomatis #
Atribut berikut diperiksa browser secara otomatis setiap form dikirim:
| Atribut | Aturan yang dijaga |
|---|---|
required | kolom tidak boleh kosong |
minlength, maxlength | jumlah karakter minimal dan maksimal |
min, max | batas nilai untuk kolom angka dan tanggal |
type="email", type="url" | format harus sesuai jenisnya |
pattern | isi harus cocok dengan regex yang diberikan |
Selama atribut-atribut ini terpasang, browser menjadi penjaga pertama: bila ada yang gagal, pesan bawaannya tampil dan peristiwa submit tidak dikirim — handler kita tidak berjalan untuk data yang belum lolos. Atribut pattern cocok terhadap seluruh isi, seolah pola diapit ^ dan $; kita memakai [A-Z]{3}-[0-9]{3} sehingga ABC-123 lolos, tetapi xyz-777 tidak, karena pola itu peka huruf besar-kecil.
<form id="form-produk">
<label for="kode">Kode Kupon</label>
<input id="kode" name="kode" type="text" required pattern="[A-Z]{3}-[0-9]{3}">
<p id="galat-kode" hidden></p>
<label for="email">Email</label>
<input id="email" name="email" type="email" required minlength="6">
<p id="galat-email" hidden></p>
<button type="submit">Tukar Kupon</button>
</form>Objek validity: Laporan Detail Tiap Kolom #
Setiap kontrol form membawa properti validity — object berisi satu boolean per jenis kegagalan, ditambah valid yang bernilai true bila semuanya lolos. Daftar lengkapnya:
| Flag | Aktif ketika |
|---|---|
valueMissing | kolom required ditinggal kosong |
typeMismatch | isi tidak sesuai jenis kolom (email, url) |
patternMismatch | isi tidak cocok dengan atribut pattern |
tooShort, tooLong | isian melewati minlength/maxlength yang berlaku |
rangeUnderflow, rangeOverflow | angka di luar batas min/max |
stepMismatch | angka tidak sesuai kelipatan step |
badInput | browser tidak bisa mengubah isian menjadi angka |
customError | setCustomValidity() sedang menandai kegagalan |
valid | tidak ada flag lain yang aktif |
Membaca flag yang relevan jauh lebih informatif daripada sekadar tahu “tidak valid”, karena pesan yang kita tampilkan bisa sesuai penyebabnya — dan pemetaan flag ke pesan Bahasa Indonesia hanya butuh satu object kecil.
// validasi-bawaan.js — validity state, checkValidity, dan setCustomValidity
const form = document.querySelector("#form-produk");
const kode = form.querySelector("#kode");
const email = form.querySelector("#email");
// aturan tambahan milik toko: kupon XYZ-777 sudah pernah ditukar
function periksaKupon() {
if (kode.value === "XYZ-777") {
kode.setCustomValidity("Kupon XYZ-777 sudah pernah ditukar.");
} else {
kode.setCustomValidity(""); // string kosong = tanpa aturan khusus
}
}
// peta flag validity menjadi pesan Bahasa Indonesia
const PESAN = {
valueMissing: "Kolom wajib diisi.",
patternMismatch: "Format tidak sesuai pola.",
typeMismatch: "Format email belum benar.",
tooShort: "Isian terlalu pendek.",
customError: "Kupon sudah pernah ditukar.",
};
function pesanUntuk(kolom) {
for (const [flag, teks] of Object.entries(PESAN)) {
if (kolom.validity[flag]) return teks;
}
return "";
}
// --- kondisi awal: kedua kolom kosong ---
console.log("kode valid:", kode.checkValidity());
console.log("email valid:", email.checkValidity());
console.log("form valid:", form.checkValidity());
// isi salah, lalu periksa flag satu per satu
kode.value = "xyz-777";
email.value = "budi@@salah";
periksaKupon();
console.log("kode patternMismatch:", kode.validity.patternMismatch);
console.log("pesan kode :", pesanUntuk(kode));
console.log("email typeMismatch :", email.validity.typeMismatch);
console.log("reportValidity kode :", kode.reportValidity());
// format benar, tetapi kupon masuk daftar yang sudah dipakai
kode.value = "XYZ-777";
email.value = "[email protected]";
periksaKupon();
console.log("customError :", kode.validity.customError);
console.log("pesan kode :", pesanUntuk(kode));
console.log("kode valid :", kode.checkValidity());
// ganti dengan kupon yang masih tersedia
kode.value = "ABC-123";
periksaKupon();
console.log("kode valid :", kode.checkValidity());
console.log("form valid :", form.checkValidity());
// --- bukti: submit hanya terpicu bila seluruh form valid ---
let terkirim = 0;
form.addEventListener("submit", function (event) {
event.preventDefault();
terkirim += 1;
});
form.requestSubmit();
console.log("submit terpicu :", terkirim);Output yang Dihasilkan (console browser):
kode valid: false
email valid: false
form valid: false
kode patternMismatch: true
pesan kode : Format tidak sesuai pola.
email typeMismatch : true
reportValidity kode : false
customError : true
pesan kode : Kupon sudah pernah ditukar.
kode valid : false
kode valid : true
form valid : true
submit terpicu : 1Mari kita baca alurnya:
- Baris 31–34 (kondisi awal): kedua kolom kosong sehingga
valueMissingaktif;checkValidity()mengembalikanfalsetanpa menampilkan apa pun, dan versi untuk seluruh form tersedia lewatform.checkValidity(). - Baris 36–43 (isian salah):
xyz-777gagal pola karena huruf kecil, danbudi@@salahgagal format email. ObjectPESANdipetakan ke flag lewatpesanUntuk(), sehingga pesan selalu sesuai penyebab kegagalan. - Baris 43 (
reportValidity): method ini memeriksa seperticheckValidity()dan sekaligus melaporkan hasilnya — di browser sungguhan, pesan bawaan tampil di dekat kolom dan kolom mendapat fokus. Nilai kembaliannya tetap boolean:falseberarti ada yang gagal. - Baris 45–51 (aturan khusus): format sudah benar, tetapi
setCustomValidity("Kupon XYZ-777 sudah pernah ditukar.")menandai kolom gagal dengan flagcustomError. Method ini dipanggil ulang pada setiap pemeriksaan karena browser tidak tahu kapan aturan khusus kita kedaluwarsa. - Baris 53–66 (lolos penuh): kupon baru dan email yang benar membuat
form.checkValidity()bernilaitrue, laluform.requestSubmit()berhasil memicusubmit— bukti bahwa validasi bawaan berlaku sebagai gerbang sebelum handler kita.
checkValidity dan reportValidity #
Keduanya berbagi pekerjaan pemeriksaan dan berbeda pada laporannya:
checkValidity() | reportValidity() | |
|---|---|---|
| Memeriksa keabsahan | ya | ya |
| Nilai kembalian | boolean | boolean |
| Menampilkan pesan di layar | tidak | ya (UI browser) |
| Memindahkan fokus ke kolom | tidak | ya |
checkValidity() cocok untuk keputusan di dalam kode tanpa efek tampilan; reportValidity() dipakai saat kita ingin browser menyampaikan pesan bawaannya sendiri. jsdom, sebagai lingkungan uji, hanya menjalankan sisi logikanya: nilai kembalian boolean sama, tanpa gelembung pesan yang tampak.
setCustomValidity: Aturan yang Hanya Kita Tahu #
Atribut HTML tidak bisa menilai “kupon ini sudah pernah dipakai” — informasi itu hidup di data toko. Method setCustomValidity(teks) menambahkan lapisan aturan atas apa pun yang sudah diperiksa browser: teks non-kosong membuat kolom gagal dengan flag customError, sedangkan string kosong menghapusnya. Dua hal perlu dibiasakan sejak awal. Pertama, panggil setCustomValidity pada setiap putaran pemeriksaan — tandanya tidak dihitung ulang otomatis saat isian berubah, dan hanya panggilan berikutnya yang menggantinya; itulah sebabnya periksaKupon() selalu dipanggil setiap kali isian diperbarui. Kedua, cabut tandanya dengan string kosong begitu syarat terpenuhi — bila tidak, kolom yang sudah benar tetap gagal tanpa alasan yang terlihat.
form.novalidate adalah saklar kebalikan dari semua yang dibahas di sini: atribut novalidate pada <form> mematikan validasi bawaan sehingga handler submit menerima semua isian. Saklar ini dipakai ketika kita sepenuhnya mengambil alih tampilan pesan — misalnya menampilkan galat inline ala chapter sebelumnya — sambil tetap memanfaatkan validity untuk tahu apa yang salah. Pada chapter berikutnya, gerbang validasi ini kita hubungkan ke pengiriman data yang lengkap.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
validasi-bawaan.js
Unduh berkas// validasi-bawaan.js — validity state, checkValidity, dan setCustomValidity
const form = document.querySelector("#form-produk");
const kode = form.querySelector("#kode");
const email = form.querySelector("#email");
// aturan tambahan milik toko: kupon XYZ-777 sudah pernah ditukar
function periksaKupon() {
if (kode.value === "XYZ-777") {
kode.setCustomValidity("Kupon XYZ-777 sudah pernah ditukar.");
} else {
kode.setCustomValidity(""); // string kosong = tanpa aturan khusus
}
}
// peta flag validity menjadi pesan Bahasa Indonesia
const PESAN = {
valueMissing: "Kolom wajib diisi.",
patternMismatch: "Format tidak sesuai pola.",
typeMismatch: "Format email belum benar.",
tooShort: "Isian terlalu pendek.",
customError: "Kupon sudah pernah ditukar.",
};
function pesanUntuk(kolom) {
for (const [flag, teks] of Object.entries(PESAN)) {
if (kolom.validity[flag]) return teks;
}
return "";
}
// --- kondisi awal: kedua kolom kosong ---
console.log("kode valid:", kode.checkValidity());
console.log("email valid:", email.checkValidity());
console.log("form valid:", form.checkValidity());
// isi salah, lalu periksa flag satu per satu
kode.value = "xyz-777";
email.value = "budi@@salah";
periksaKupon();
console.log("kode patternMismatch:", kode.validity.patternMismatch);
console.log("pesan kode :", pesanUntuk(kode));
console.log("email typeMismatch :", email.validity.typeMismatch);
console.log("reportValidity kode :", kode.reportValidity());
// format benar, tetapi kupon masuk daftar yang sudah dipakai
kode.value = "XYZ-777";
email.value = "[email protected]";
periksaKupon();
console.log("customError :", kode.validity.customError);
console.log("pesan kode :", pesanUntuk(kode));
console.log("kode valid :", kode.checkValidity());
// ganti dengan kupon yang masih tersedia
kode.value = "ABC-123";
periksaKupon();
console.log("kode valid :", kode.checkValidity());
console.log("form valid :", form.checkValidity());
// --- bukti: submit hanya terpicu bila seluruh form valid ---
let terkirim = 0;
form.addEventListener("submit", function (event) {
event.preventDefault();
terkirim += 1;
});
form.requestSubmit();
console.log("submit terpicu :", terkirim);