Kurangnya Pemanfaatan Validasi Form HTML
(expressionstatement.com)- Form HTML sudah memiliki fitur validasi native seperti
required,type="email",pattern,maxlength, dansetCustomValidity, tetapi pemanfaatannya di praktik nyata masih rendah dibandingkan luasnya kemampuan yang tersedia setCustomValidityyang paling kuat dapat menangani logika validasi arbitrer dan kasus kompleks, tetapi hanya tersedia sebagai metode DOM, sehingga kurang cocok dengan komponen deklaratif- Di lingkungan seperti React, untuk menyelaraskan validasi nilai awal,
useRef,useLayoutEffect, danonChangemenjadi saling terkait, dan logika validasi yang sama mudah terduplikasi di rendering awal dan handler perubahan - Jika ada abstraksi berbasis atribut seperti
custom-validity, validasi yang memiliki dependensi antara state dan input, seperti pemeriksaan asinkron duplikasi nama pengguna atau konfirmasi kata sandi, dapat diekspresikan di satu tempat - Rendahnya adopsi validasi form native lebih dekat ke masalah kemudahan penggunaan API daripada kurangnya fitur, dan jika API deklaratif masuk ke spesifikasi HTML, ruang pemanfaatannya akan makin besar
Apa yang Sudah Disediakan Validasi Form HTML
- Cara paling sederhana untuk mencegah input kosong adalah menambahkan atribut
required - Batasan input dapat diterapkan secara garis besar dengan tiga cara
- Menggunakan tipe input seperti
type="email",type="number",type="url" - Menggunakan atribut batasan seperti
pattern,maxlength - Menggunakan metode DOM
setCustomValiditypada input
- Menggunakan tipe input seperti
setCustomValidityadalah cara paling kuat karena dapat menangani logika validasi arbitrer dan kasus kompleks- Dua cara pertama dapat dideklarasikan sebagai atribut HTML, tetapi
setCustomValidityberbeda karena memerlukan pemanggilan metode - Sebagai referensi tentang perbedaan atribut dan properti DOM, ditautkan Attributes vs Properties
Titik yang Membuat setCustomValidity Merepotkan
setCustomValidityhanya diekspos sebagai metode tanpa atribut HTML yang sesuai- Jika sebuah string diberikan, input menjadi dalam status invalid, dan browser menampilkan string tersebut sebagai alasan kegagalan validasi
- Jika string kosong diberikan, input menjadi valid selama tidak ada batasan lain
- Untuk mengimplementasikan sendiri perilaku seperti
required,setCustomValidityharus dipanggil setiap kali nilai berubah - Namun, input awalnya berada dalam status valid, sehingga jika tombol submit ditekan tepat setelah komponen direset, pengiriman form bisa lolos
- Sebab, meskipun nilainya kosong, kode validasi belum berjalan sebelum event perubahan terjadi
- Untuk menyelaraskan hingga nilai awal, validasi yang sama juga harus dijalankan saat komponen di-mount
Boilerplate yang Bertambah pada Komponen Deklaratif
- Jika validasi nilai awal juga harus ditangani, kode dengan cepat menjadi repetitif dan merepotkan
- Khususnya, muncul tiga masalah
- Logika validasi terduplikasi di handler
onChangedan tahap rendering awal - Kode validasi awal terpisah dari elemen input, sehingga kohesinya menurun dan ada risiko hanya salah satu sisi yang diperbarui
- Kombinasi
useRef,useLayoutEffect, danonChangeterasa berlebihan saat jumlah input bertambah, dan makin membingungkan ketika hanya sebagian input yang memakaicustomValidity
- Logika validasi terduplikasi di handler
- Kompleksitas seperti ini menonjol saat menangani API imperatif murni di komponen deklaratif
CustomValiditytidak memiliki atribut input untuk menetapkan nilai secara deklaratif seperti atribut validasi native- Jika API merepotkan, adopsi bisa rendah meskipun fiturnya kuat, dan poin inilah penyebab utama kurangnya pemanfaatan validasi form native
Bagian yang Hilang: Atribut custom-validity
- Bentuk yang dibutuhkan adalah atribut deklaratif seperti
custom-validityyang dapat ditentukan langsung pada input - Dalam framework deklaratif, atribut ini memungkinkan status validasi ditempatkan dekat dengan elemen input
- Saat ini,
HTML Spectidak memilikicustom-validitysungguhan - Implementasi di sisi pengguna untuk tujuan demo dapat meniru perilaku ini
- Sebagai contoh komponen untuk produksi, tersedia implementasi yang lebih lengkap
Validasi Asinkron dan Dependensi Antar-Input
- Validasi pada aplikasi nyata bisa lebih kompleks daripada pemeriksaan lokal
- Input nama pengguna harus memeriksa ke server apakah nama tersebut sudah digunakan, dan selama request berlangsung form tidak boleh berada dalam status valid
- Contohnya menggunakan
requireduntuk mencegah input kosong, lalu menjadikan input invalid dengancustomValiditysesuai status loading dan hasil respons - Implementasinya terdiri dari dua bagian
- Mengelola status request pemeriksaan keunikan nama pengguna dengan
useQuerydarireact-query - Menggunakan komponen
<Input />kustom yang dapat menerima propcustomValidity
- Mengelola status request pemeriksaan keunikan nama pengguna dengan
- Cara ini dapat mengekspresikan seluruh alur validasi asinkron, termasuk status loading, error, dan sukses, dalam satu atribut
- Contoh tambahan adalah form yang meminta pengguna memasukkan ulang kata sandi, yang menangani validasi field input yang saling bergantung
Kesimpulan
setCustomValidityadalah alat yang kuat untuk menangani berbagai kebutuhan validasi- Yang menentukan tingkat pemanfaatan nyata bukan hanya keberadaan fitur, tetapi juga API yang membuat fitur tersebut mudah digunakan
- Abstraksi deklaratif seperti
custom-validitymembuat validasi form native dapat digunakan dengan lebih natural - Fitur seperti ini dapat diharapkan suatu hari masuk secara native ke spesifikasi HTML
1 komentar
Opini Hacker News
Terakhir kali saya cek, peramban web modern masih tidak bisa menata tampilan pesan validasi HTML bawaan https://stackoverflow.com/questions/5328883/how-do-i-style-t...
Kalau saja Chrome dan Firefox mengikuti panduan UI platform OS sehingga terlihat seperti sesuatu yang dimunculkan sistem, seperti tooltip
title="", mungkin tidak terlalu buruk. Namun Chrome memakai ikon kuning/oranye, teks hitam di latar putih, dan balon dengan sudut membulat tetap, sehingga sangat berbenturan dengan estetika proyek saat iniChrome lama pernah mengizinkan styling pesan validasi dengan selector pseudo-elemen berprefiks vendor, tetapi setelah menghapus fitur itu, mereka tidak pernah mengembalikannya. Ini jadi masuk ke daftar kekesalan acak seperti “tolong beri kami combobox HTML native”, atau “kenapa masih berupa kotak Ctrl+klik yang sulit dipakai, bukannya daftar checkbox”
validitynative dari input lalu merendernya sesuka kita sebenarnya cukup mudahMasalah sebenarnya adalah sulitnya berlangganan secara akurat ke perubahan status validitas ini. Event validitas memang ada, tetapi tidak selalu terjadi. Jika status form diubah secara programatik, seperti memanggil
form.reset()atauinput.value = '...', event-event ini tidak dipicuMenurut saya ini topik yang bagus untuk diteliti terpisah dan diajukan ke platform web
Hal terbesar, mudah diimplementasikan, tetapi kurang digunakan adalah memakai nilai atribut
typeyang spesifik sepertiemail,number, danurlDi mobile, ini bisa menampilkan keyboard yang optimal dan sangat meningkatkan pengalaman pengguna
type=numberdan memakaitype=text inputmode=numericsebagai gantinya. Tidak ada tombol panah yang tidak dibutuhkan kebanyakan pengguna untuk input angka, dan keyboard di iOS juga lebih baiktypeyang benardate. Setiap library frontend punya widget tanggalnya sendiri, dan banyak yang tampil buruk di layar kecilDemi satu widget itu, JS dan CSS tambahan harus dimuat, dan sebagian bergantung pada jQuery. Tentu ada juga yang sangat bisa dikonfigurasi, tetapi dengan biaya yang sangat kecil kita bisa mendapatkan widget yang tampak cukup baik di mana-mana dan terasa native, biasanya memenuhi kebutuhan, serta bisa dilupakan tanpa khawatir soal pembaruan atau CDN
Dengan input biasa saja nyaris bisa, sedangkan input khusus mendukung event yang lebih sedikit
Produk saya tidak lolos audit aksesibilitas karena menggunakan validasi form HTML native, dan rekomendasi resminya adalah mengimplementasikan lapisan validasi sendiri. Saya juga setuju dengan rekomendasi itu
Validasi HTML native punya banyak kekurangan, dan jujur saja, kustomisasi visual bahkan bukan kekhawatiran terbesar. Meski begitu, itu memang seperti paku terakhir di peti mati
Misalnya, kalau ingin menampilkan beberapa error per field sekaligus, satu-satunya cara adalah menggabungkan string. Contohnya seperti “Angka diperlukan. Simbol diperlukan. Harus lebih panjang dari 10 karakter”, dan ini buruk untuk pengalaman pengguna maupun aksesibilitas. Sebab string yang digabungkan itu tidak bisa dinavigasi di accessibility tree. Ini bukan masalah implementasi tertentu, melainkan masalah pada spesifikasi itu sendiri. Pengguna tidak senang bermain whack-a-mole dengan error validasi, jadi kita harus bisa menampilkan beberapa error sekaligus
Ketergantungan pada browser juga buruk. Kita tidak bisa mengendalikannya, dan implementasinya umumnya buruk. Chrome menampilkan popup saat fokus, dan karena itu popup, tampak seperti modal, serta tidak bisa menampilkan semua error form sekaligus, hal itu sendiri tidak ramah aksesibilitas. Tidak menampilkan informasi penting dalam popup adalah dasar aksesibilitas, tetapi browser tidak punya banyak opsi lain tanpa mengganggu dokumen sebenarnya
Error tingkat seluruh form yang tidak termasuk ke field tertentu juga tetap membutuhkan validasi kustom. Misalnya error seperti “Field A dan B tidak kompatibel”, dan kalau sudah begitu, lebih baik punya cara validasi yang konsisten
Jika ada input kustom yang tidak cocok dengan tipe input native, demi konsistensi kita perlu membuat input tersembunyi, dan ini juga merusak aksesibilitas. Untuk memperbaikinya, pada akhirnya sama sulitnya dengan membuat sistem validasi sendiri yang ramah aksesibilitas
API
customValiditybersifat imperatif dan merepotkan digunakan. Kecuali Anda menginginkan pesan buruk seperti “Field ini tidak valid”, hampir tidak ada pilihan untuk tidak memakai validitas kustom. Karena itu validasi form HTML memang burukMeski begitu, menurut saya mekanisme validasi native tetap layak dimanfaatkan. Untuk pesan error, kita tidak harus memakai tooltip validitas native; kita bisa membaca
ValidityStatedariinput.validitysecara langsung, lalu merender pesan dengan cara yang diinginkan dan menampilkan beberapa error bila perluBrowser bisa membaik, dan kalau memakai pendekatan yang terstandardisasi, kita bisa mendapatkan manfaatnya. Kesimpulan seperti “kalau tidak memakai popup maka aksesibilitas rusak” terdengar aneh, tetapi jika perlu memenuhi audit, mungkin memang harus begitu
Ada juga teknik untuk menangani error yang tidak termasuk ke field tertentu. Di proyek saya, kami memakai komponen
HiddenValidationInputyang membuat input read-only tak terlihat untuk merender error kustom yang tidak termasuk ke field lain. Cukup dirender secara kondisional, jadi dalam praktiknya cukup nyaman digunakanSaya sepenuhnya setuju bahwa API
customValiditybersifat imperatif dan merepotkan. Saya menulis artikel yang membahas tepat bagian itu, dan saya berharap bagian ini juga membaik seiring waktuDesain Chrome yang kasar dan kurang aksesibel adalah masalah Chrome, jadi saya akan mengirim laporan bug
Apakah yang dimaksud dengan “spesifikasi” adalah ini? https://html.spec.whatwg.org/multipage/dom.html#concept-elem...
Saya tidak melihat bagian yang mendefinisikan bagaimana pesan validasi harus ditampilkan. Bahkan tidak ada ketentuan bahwa pesan harus ada
Terus terang, orang-orang yang menulis spesifikasi seperti ini tampaknya terlepas dari realitas dan tidak benar-benar memakai apa yang mereka spesifikasikan. Ini berfungsi untuk hal yang sangat sederhana, tetapi begitu form mulai berevolusi, kita akhirnya sadar bahwa lebih baik menulis semuanya sendiri
Begitu mulai memakai JS, jauh lebih mudah mengerjakan semuanya dengan kode daripada mengutak-atik atribut validasi
Kalau checkbox punya label, saya berharap label diberi atribut
forsupaya checkbox bisa dinyalakan dan dimatikan dengan mengeklik label. Secara pribadi ini salah satu hal yang paling mengganggu saya, meski mungkin cuma saya sajainputdenganlabeljuga berfungsi. Saya tidak terlalu paham kenapa orang cenderung memisahkan keduanyaDan saya juga tidak tahu kenapa browser mulai memisahkan hal-hal ini. Menurut saya bukan teks yang berisi checkbox dan radio, melainkan checkbox dan radio yang seharusnya berisi teks
Contoh sederhana yang tidak memakai React ada di sini
https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectE...
Validasi formulir HTML itu bagus. Hanya saja ada satu jebakan yang sangat besar
Tidak berfungsi di Firefox untuk Android
https://bugzilla.mozilla.org/show_bug.cgi?id=1510450
Tidak sepadan mengabaikan standar demi kurang dari 1% pengguna yang tidak bisa melihat pesan error pada kontrol yang ditandai merah. Semakin banyak situs web yang memakai ini, semakin besar pula tekanan bagi Mozilla untuk memperbaiki browser-nya
Ini bukan API seperti WebMIDI yang diimplementasikan Chrome atau Safari sebelum distandardisasi, melainkan bagian dari spesifikasi HTML5 sejak awal
Apalagi jika mengingat betapa sedikitnya waktu yang dihabiskan untuk memastikan orang yang menggunakan software aksesibilitas bisa memakai situs dengan benar. Kalau belum siap membahas sampai UC Browser, menurut saya isu seperti ini tidak terlalu layak disebut
Meski begitu, bisa memakai uBlock Origin bersama ekstensi lain tetap merupakan keunggulan kuat
[1] https://bugzilla.mozilla.org/show_bug.cgi?id=1884282
[2] https://bugzilla.mozilla.org/show_bug.cgi?id=1897707
Kalau validasinya memang tidak diimplementasikan sama sekali, itu justru masih lebih baik; yang ini benar-benar kacau. Saya mengetahuinya beberapa tahun lalu setelah proses debugging yang panjang dan menyakitkan, dan saya yakin saya bukan orang pertama maupun terakhir yang mengalami hal yang sama
Sebelum mencari tahu, saya sama sekali tidak bisa membayangkan apa yang terlewat, dan itu cukup mengejutkan. Membuat validasi sendiri tidak masalah, tetapi yang ini seharusnya berfungsi
Harus hati-hati agar tidak memakainya secara berlebihan
Baru-baru ini saya mencoba mendapatkan refund dari Groupon. Alasannya, usaha tempat saya membeli Groupon sudah berganti manajemen dan mereka tidak mengakui Groupon saya
Formulirnya punya syarat “minimal 15 kata”, tetapi apa pun yang saya lakukan validasinya tidak lolos, jadi akhirnya saya harus menginspeksi HTML-nya
\wadalah karakter kata,\badalah batas kata,\sadalah spasi, dan tanda baca sama sekali tidak diizinkan secara literalpatternmemang keren, tetapi tidak cukupMisalnya, contoh “masukkan ulang kata sandi” bisa diimplementasikan dengan atribut
patterntanpasetCustomValidity. Untuk itu, regex harus disusun secara dinamis dari nilai input pertamaSaya tidak membandingkan solusi-solusinya karena khawatir tulisannya jadi terlalu panjang, tetapi intinya adalah dengan
customValidity, validasi menjadi jauh lebih jelas dan mudah dibaca. Di sini, API yang lebih baik membuat perbedaan besarBatasan “minimal 15 kata” juga bisa diekspresikan dengan jauh lebih enak dibaca, seperti
value.split(/\s+/).length >= 15Ada alasan mengapa ini jarang dipakai. Banyak framework dan library menyediakan fitur validasi yang tangguh dan bisa di-styling, dan sebagian sangat canggih serta dapat diperluas. Kalau tidak perlu bersusah payah, sebaiknya jangan menyiksa diri sendiri
curl, atau alat lain yang tidak punya validasi form bawaan yang samaKarena kita tidak bisa percaya bahwa klien sudah melakukan validasi, atau melakukannya dengan benar, backend tetap harus memvalidasi input dan mampu menampilkan form beserta error validasi untuk semua field
Validasi frontend hanyalah untuk membantu pengguna. Namun kalau ingin men-styling-nya atau memicunya dari backend saat submit, pada akhirnya harus mengimplementasikan styling sendiri juga
Terlalu banyak jebakan, dan untuk mendukung pemeriksaan yang lebih kompleks dengan mudah, pada akhirnya tetap memakai library
Selain itu, jika memakai library, dalam beberapa kasus ada kemungkinan berbagi sebagian kode validasi antara frontend dan backend
Khususnya tulisan ini tampaknya mengakali satu masalah dengan
useLayoutEffect, dan itu bukan hal yang sebaiknya dilakukan sembaranganSalah satu hal yang saya tidak suka dari validasi formulir HTML adalah validasi itu berjalan sejak halaman dimuat. Misalnya, jika styling status error diikat ke sini, form bisa dimuat dalam keadaan penuh error sejak awal dan terasa mengintimidasi bagi pengguna
:user-invalidyang membantu menghindari ini sampai batas tertentu. Namun ada bagian yang kurang fleksibel, jadi mungkin tidak cukup tergantung use casehttps://developer.mozilla.org/en-US/docs/Web/CSS/:user-inval...
Memang bisa diakali dengan script, tetapi pada titik itu fitur ini tidak lagi banyak membantu. Menurut saya ini alasan terbesar mengapa fitur ini tidak diadopsi lebih luas