Prompt dan Konteks yang Berlaku
Bangun alur validasi untuk formulir checkout yang berisi email, negara, kode pos, tanggal pengiriman, dan kode diskon opsional. Aturan kode pos bergantung pada negara. Kode diskon diperiksa oleh layanan jarak jauh. Server tetap menjadi pihak yang berwenang (otoritatif) untuk harga, stok, aturan alamat, dan validitas kode.
Formulir harus berfungsi dengan keyboard dan teknologi asistif, pada tingkat perbesaran 200%, serta setelah round trip ke server. Kesalahan tidak boleh disampaikan hanya melalui warna. Pengiriman yang gagal harus mempertahankan nilai yang telah dimasukkan, mengidentifikasi setiap kesalahan yang dapat ditindaklanjuti, dan menyediakan jalur pemulihan langsung. Kegagalan jaringan, kode yang kedaluwarsa, dan respons asinkron baru yang mendahului respons lama adalah bagian dari masalah yang harus ditangani.
Tujuannya bukan untuk menciptakan pustaka formulir baru. Jawabannya harus mendefinisikan state, waktu validasi, hubungan semantik, kebijakan fokus, dan kontrak kesalahan klien-server, kemudian menunjukkan bagaimana keputusan-keputusan tersebut diverifikasi. Batasan bawaan HTML adalah fondasi dasarnya; perilaku kustom harus menambah kejelasan tanpa menghilangkan semantik browser.
Apa yang Dinilai oleh Pewawancara
Sinyal pertama adalah tanggung jawab. Validasi sisi klien memberikan umpan balik cepat dan menghindari permintaan yang tidak perlu. Validasi server menentukan apakah suatu pesanan diterima. Kandidat yang mempercayai atribut required di sisi klien atau total diskon di sisi klien telah meninggalkan celah keamanan dan kebenaran data.
Sinyal kedua adalah pemulihan, bukan sekadar deteksi. Border yang menandakan tidak valid tidaklah cukup. Setiap kesalahan memerlukan teks biasa, hubungan terprogram ke kontrolnya, dan instruksi perbaikan. Saat pengiriman, pengguna memerlukan ringkasan gambaran umum dan penempatan fokus yang dapat diprediksi. Input valid yang mereka masukkan harus tetap utuh.
Sinyal ketiga adalah pemilihan waktu (timing). Menampilkan "tidak valid" saat pengguna masih mengetik email akan menimbulkan gangguan. Lakukan validasi saat submit terlebih dahulu; setelah suatu bidang gagal, validasi ulang saat blur atau saat terjadi perubahan yang bermakna sehingga pengguna dapat melihat bahwa perbaikan telah berhasil. Validasi asinkron memerlukan status pending dan perlindungan terhadap respons usang (stale response).
Sinyal terakhir adalah verifikasi. Pemindaian aksesibilitas otomatis dapat menemukan label yang hilang, tetapi tidak dapat membuktikan kewajaran pengumuman pembaca layar, urutan fokus, pemulihan setelah respons server, atau perlindungan dari respons kode diskon yang datang tidak berurutan. Hal-hal tersebut memerlukan pengujian interaksi.
Pertanyaan Klarifikasi Sebelum Menjawab
- Pemeriksaan mana saja yang bersifat lokal? Keberadaan nilai, format, dan aturan sederhana antar-bidang dapat dijalankan secara lokal. Harga, stok, kelayakan, dan validitas akhir diskon sepenuhnya ditentukan oleh server.
- Kapan umpan balik harus muncul? Submit menampilkan semua kesalahan yang memblokir. Bidang yang sudah pernah gagal dapat divalidasi ulang saat blur atau setelah perubahan yang disengaja; jangan membuat pengumuman pembaca layar pada setiap penekanan tombol.
- Apakah server akan merender ulang halaman? Baik pengiriman formulir dengan progressive enhancement maupun biasa harus mempertahankan nilai dan merender kesalahan terstruktur yang sama.
- Bisakah satu kesalahan memengaruhi beberapa kontrol? Negara dan kode pos membentuk satu kesatuan aturan. Letakkan instruksi bersama di dekat grup tersebut dan tautkan ringkasan ke kontrol yang memulai perbaikan.
- Apa yang terjadi selama validasi diskon? Status pending-nya bersifat informatif, bukan kesalahan. Nilai saat ini dan nomor generasi permintaan menentukan apakah suatu respons masih relevan.
- Ke mana fokus harus berpindah setelah terjadi kegagalan? Untuk beberapa kesalahan, fokuskan ke ringkasan sebelum formulir; untuk kasus kesalahan tunggal yang ringkas, memfokuskan ke kontrol yang tidak valid dapat diterima. Pilih satu kebijakan dan hindari mengumumkan perubahan yang sama dua kali.
- Kegagalan apa saja yang bukan merupakan kesalahan bidang? Gangguan jaringan atau perubahan stok termasuk dalam pesan tingkat formulir dengan opsi coba lagi. Jangan melampirkannya ke email atau kode pos.
- Apakah alur ini mengekspos data sensitif? Formulir autentikasi dan pemulihan akun mungkin memerlukan pesan server yang bersifat umum. Perbaikan yang membantu tidak boleh membocorkan apakah suatu akun terdaftar atau tidak.
Kerangka Jawaban 30 Detik
“Saya memulai dengan kontrol dan batasan bawaan yang berlabel, tetapi server tetap menjadi pemegang otoritas tertinggi. Setiap kontrol yang tidak valid mendapatkan aria-invalid="true" dan teks kesalahan stabil yang direferensikan oleh aria-describedby; warna dan ikon bersifat sekunder. Pengiriman gagal pertama kali akan merender ringkasan kesalahan yang berisi tautan ke setiap bidang tidak valid dan memindahkan fokus ke ringkasan tersebut. Nilai yang dimasukkan tetap dipertahankan.
Saya melakukan validasi saat submit, kemudian saat blur atau perubahan yang bermakna hanya untuk bidang yang telah gagal. Pemeriksaan kode diskon menggunakan debounce, dapat dibatalkan, dan diberi tag generasi sehingga respons lama tidak dapat menggantikan nilai baru. Server mengembalikan kesalahan bidang yang teridentifikasi ditambah kesalahan tingkat formulir dalam bentuk yang terstruktur. Saya menguji pemulihan keyboard dan screen reader, perbesaran serta forced colors, round trip server, pengiriman saat JavaScript dinonaktifkan, dan async race di samping pemeriksaan otomatis.”
Pembahasan Mendalam Langkah demi Langkah
Langkah 1: Definisikan state dan invarian
Pisahkan nilai input dari status validasi. Setiap bidang dapat berstatus belum disentuh (untouched), tertunda (pending), valid, atau tidak valid, dengan kode kesalahan yang dipetakan ke teks terjemahan lokal. Kesalahan tingkat formulir dipisahkan tersendiri. Simpan flag upaya pengiriman agar antarmuka tidak menampilkan bidang yang baru diketik setengah sebagai bidang yang gagal.
Invariannya adalah: label tetap terlihat; instruksi tersedia sebelum terjadi kesalahan; setiap kesalahan bidang yang ditampilkan terhubung secara terprogram; kesalahan yang tersembunyi tidak direferensikan; satu tindakan pengiriman menghasilkan satu perpindahan fokus yang jelas; input yang valid tetap bertahan saat terjadi kegagalan; dan respons asinkron yang usang tidak dapat mengubah state saat ini.
Langkah 2: Bangun kontrol semantik sebelum ARIA
Gunakan label, tipe input yang paling spesifik, required, autocomplete, serta batasan panjang atau pola yang sesuai. Aturan kode pos yang bergantung pada negara dapat menggunakan setCustomValidity(). Hapus pesan kustom dengan string kosong segera setelah nilainya menjadi valid; jika tidak, browser akan terus memblokir pengiriman formulir.
<label for="email">Email</label>
<input id="email" name="email" type="email"
aria-invalid="true"
aria-describedby="email-hint email-error">
<p id="email-hint">name@example.com</p>
<p id="email-error">Enter a valid email address.</p>checkValidity() memeriksa batasan, sedangkan reportValidity() juga meminta browser untuk menampilkan umpan baliknya. Jika produk merender pesan aksesibelnya sendiri, lakukan intersept terhadap status tidak valid secara konsisten daripada menampilkan dua sistem kesalahan yang saling bertabrakan.
Langkah 3: Pilih waktu validasi secara sengaja dan terencana
Pada pengiriman pertama, validasi setiap bidang dan tampilkan semua kesalahan yang memblokir. Setelah itu, validasi ulang bidang yang tidak valid saat terjadi blur. Untuk pilihan dropdown atau nilai yang diperbaiki dengan bentuk lengkap yang tidak ambigu, perubahan dapat menghapus kesalahan lebih cepat. Jangan mengirim pengumuman live-region berulang kali saat tanggal atau email belum selesai diketik.
Aturan lintas-bidang dijalankan ketika salah satu dependensi berubah. Jika negara berubah, validasi ulang kode pos dan perbarui instruksi visualnya. Jangan menulis ulang input pengguna secara diam-diam kecuali jika normalisasi tersebut aman dan dapat dibalikkan; memangkas spasi di awal/akhir berbeda dengan menebak-nebak format kode pos.
Langkah 4: Tampilkan kesalahan inline dan ringkasan yang dapat dinavigasi
Render teks ringkas di samping setiap bidang, tetapkan aria-invalid hanya saat tidak valid, dan referensikan ID stabil milik pesan kesalahan tersebut. Pastikan tampilan visual tetap dapat digunakan dalam mode forced-colors dan sertakan kata-kata, bukan hanya border merah atau ikon peringatan.
Setelah pengiriman dengan banyak kesalahan gagal, sisipkan ringkasan dengan judul sebelum formulir. Berikan target fokus sementara, fokuskan sekali, sebutkan jumlah kesalahan, dan sediakan tautan ke kontrol terkait. Teks tautan harus menyebutkan nama bidang dan perbaikannya. Jangan memindahkan fokus ke bidang pertama pada event yang sama; itu akan membuat ringkasan sulit untuk diperiksa.
Langkah 5: Buat validasi asinkron aman dari race condition
Tunggu hingga kode diskon selesai diketik, lalu terapkan debounce pada permintaan. Batalkan (abort) permintaan sebelumnya saat nilai berubah dan bandingkan juga nilai generasi yang meningkat secara monoton saat permintaan selesai. Kedua pemeriksaan ini penting karena pembatalan bisa saja tiba setelah respons sudah terlanjur diproses.
Tampilkan status “Memeriksa” di dekatnya melalui polite live region. Nonaktifkan tindakan penerapan diskon saat pemeriksaan sedang berlangsung, tetapi jangan menonaktifkan bidang lain yang tidak terkait. Timeout menjadi status tingkat formulir atau tingkat kode yang dapat dicoba lagi sesuai kontrak produk; hal itu tidak boleh dilaporkan sebagai “kode tidak valid”. Simpan hasil cache hanya bersama konteks harga dan masa kedaluwarsa yang membuatnya valid.
Langkah 6: Rekonsiliasi kesalahan server yang otoritatif
Kirim nilai mentah melalui action formulir biasa atau permintaan yang ditingkatkan (enhanced request). Server menormalisasi dan memvalidasi kembali, menghitung total, serta mengembalikan hasil seperti kode kesalahan bidang, kode kesalahan formulir, dan nilai yang diterima. Klien hanya memetakan nama bidang yang ada dalam allowlist. Kunci yang tidak dikenal akan menjadi kesalahan tingkat formulir yang aman, alih-alih menjadi celah injeksi pemilih (selector) atau markup.
Jika terjadi penolakan, pertahankan semua isian yang tidak sensitif, ganti asumsi sisi klien dengan kebenaran dari server, render ringkasan yang sama, dan arahkan fokus ke sana. Jika berhasil, tampilkan konfirmasi yang tidak ambigu dan cegah aktivasi ganda. Jika respons hilang setelah pengiriman, gunakan kunci idempotensi atau pencarian pesanan sebelum menyarankan penagihan ulang.
Langkah 7: Verifikasi jalur pemulihan, bukan sekadar snapshot visual
Uji pengiriman kosong, satu kesalahan, beberapa kesalahan, dependensi negara/kode pos, tanggal pengiriman yang kedaluwarsa, timeout diskon, diskon tidak valid, perubahan kode yang cepat, penolakan khusus server, dan hilangnya respons sukses. Pastikan nilai tetap ada, tautan ringkasan memfokuskan kontrol yang tepat, kesalahan yang telah diperbaiki menghilang, dan respons asinkron lama diabaikan.
Selesaikan alur secara manual hanya menggunakan keyboard dan screen reader. Periksa perbesaran 200%, reflow, fokus yang terlihat, forced colors, autofill browser, dan pengiriman biasa ke server tanpa JavaScript klien. Pengujian aksesibilitas otomatis dan unit test melengkapi pemeriksaan ini; keduanya tidak menggantikan pengujian pengumuman pembaca layar dan navigasi fokus.
Contoh Jawaban yang Kuat
“Saya akan memodelkan nilai bidang secara independen dari status disentuh, pending, dan error. Label native, tipe input, token autocomplete, dan batasan bawaan menyediakan fondasi dasarnya. Aturan lintas-bidang kustom menggunakan setCustomValidity() dan selalu menghapus pesannya saat valid. Pemeriksaan klien meningkatkan kecepatan; server memvalidasi ulang harga, stok, alamat, dan diskon sebelum menerima pesanan.
Pengiriman pertama mengungkapkan setiap kesalahan yang memblokir. Setiap input yang tidak valid menerima aria-invalid="true" dan mereferensikan teks perbaikan yang terlihat menggunakan aria-describedby. Untuk beberapa kesalahan, saya merender ringkasan sebelum formulir, memfokuskannya sekali, dan menautkan setiap item ke kontrolnya. Nilai yang valid tetap dipertahankan. Setelah submit tersebut, bidang yang gagal akan divalidasi ulang saat blur atau saat terjadi perubahan yang bermakna, tanpa pengumuman live pada setiap penekanan tombol.
Validasi diskon menggunakan debounce serta membawa abort signal dan nomor generasi. Hanya respons yang cocok dengan nilai saat ini yang dapat memperbarui UI. Status pending dan tidak tersedia dibedakan dari status tidak valid. Kode bidang dari server dipetakan melalui allowlist; kegagalan global tetap berada di tingkat formulir. Saya hanya akan merilis fitur ini setelah pengujian keyboard, screen reader, perbesaran, round-trip server tanpa JavaScript, race condition, dan pengiriman ganda berhasil dilewati bersama dengan pengujian otomatis.”
Kesalahan Umum
- Hanya menggunakan border merah → sebagian pengguna tidak dapat melihat status tersebut → tambahkan teks korektif, asosiasi terprogram, dan petunjuk non-warna.
- Memvalidasi pada setiap penekanan tombol → input yang belum selesai menghasilkan gangguan terus-menerus → validasi saat submit, lalu validasi ulang bidang yang gagal pada batasan yang tepat.
- Membiarkan
setCustomValidity()terisi → input yang sudah diperbaiki tetap dianggap tidak valid → setel ke string kosong saat aturan terpenuhi. - Memfokuskan ringkasan dan bidang pertama secara bersamaan → pengumuman pembaca layar dan fokus saling bertabrakan → buat satu perpindahan fokus yang terarah dan sediakan tautan untuk navigasi.
- Menganggap timeout sebagai kode tidak valid → kegagalan infrastruktur disalahartikan sebagai kesalahan pengguna → representasikan status pending, tidak tersedia, dan tidak valid secara terpisah.
- Menerima respons terakhir yang tiba di klien → validasi usang menimpa input saat ini → batalkan proses lama dan bandingkan generasi permintaan.
- Mempercayai total harga dari klien → permintaan dapat melewati UI → hitung ulang dan validasi di server.
- Mengosongkan formulir setelah terjadi kegagalan → proses pemulihan menjadi pengetikan ulang dari awal → pertahankan nilai valid yang tidak sensitif dan hanya perbarui status kesalahannya.
Pertanyaan Lanjutan dan Jawabannya
Pertanyaan Lanjutan 1: Haruskah ringkasan kesalahan menggunakan role="alert"?
Untuk kesalahan yang disisipkan secara dinamis, alert atau live region yang sesuai dapat mengumumkan ringkasan tersebut. Memindahkan fokus juga dapat membuatnya terbaca oleh pengguna teknologi asistif. Uji kombinasi yang dipilih karena fokus ditambah assertive alert dapat mengumumkan konten yang sama dua kali. Pada navigasi server penuh, mencantumkan jumlah kesalahan dalam judul halaman dan heading utama dapat memberikan konteks lebih awal.
Pertanyaan Lanjutan 2: Mengapa tidak menonaktifkan tombol submit sampai formulirnya valid?
Tombol yang dinonaktifkan dapat menyembunyikan apa yang masih salah dan tidak dapat dijangkau oleh beberapa mode navigasi. Biarkan jalur pengiriman tetap tersedia agar pengguna dapat meminta hasil validasi lengkap. Nonaktifkan hanya saat pengiriman yang sebenarnya sedang diproses, jelaskan status tersebut, dan pulihkan jika terjadi kegagalan.
Pertanyaan Lanjutan 3: Kapan aria-describedby lebih disukai daripada live region?
aria-describedby memberikan petunjuk dan pesan kesalahan saat ini kepada bidang terkait ketika pengguna bernavigasi ke bidang tersebut. Live region mengumumkan perubahan dinamis yang bermakna tanpa perpindahan fokus. Kesalahan inline yang statis tidak semuanya memerlukan live region; mengumumkan setiap bidang sekaligus menimbulkan kebisingan audio. Gunakan ringkasan untuk kesalahan massal dan status sopan (polite) untuk perubahan yang benar-benar asinkron.
Pertanyaan Lanjutan 4: Bagaimana cara mendukung validasi yang dirender server tanpa JavaScript?
Gunakan action formulir HTML standar. Server mengembalikan halaman yang sama dengan nilai-nilai yang tetap dipertahankan, kode kesalahan bidang, ringkasan sebelum formulir, dan jumlah kesalahan pada judul halaman atau heading. Tautan ringkasan menggunakan ID kontrol yang stabil. Peningkatan di sisi klien (client enhancement) harus menggunakan kontrak kesalahan yang sama, sehingga kedua mode tersebut tidak menyimpang.
Pertanyaan Lanjutan 5: Bagaimana cara menguji async race secara deterministik?
Kendalikan dua Promise validasi. Mulai permintaan A, ubah nilainya, lalu mulai permintaan B. Selesaikan B sebagai valid dan selesaikan A kemudian sebagai tidak valid. Pastikan bahwa UI tetap mencerminkan B dan nilai input saat ini. Ulangi pengujian dengan skenario pembatalan (abort), timeout, unmount komponen, dan pengiriman ulang saat status masih pending.