2 poin oleh GN⁺ 2024-10-29 | 1 komentar | Bagikan ke WhatsApp
  • Form HTML sudah memiliki fitur validasi native seperti required, type="email", pattern, maxlength, dan setCustomValidity, tetapi pemanfaatannya di praktik nyata masih rendah dibandingkan luasnya kemampuan yang tersedia
  • setCustomValidity yang 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, dan onChange menjadi 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 setCustomValidity pada input
  • setCustomValidity adalah cara paling kuat karena dapat menangani logika validasi arbitrer dan kasus kompleks
  • Dua cara pertama dapat dideklarasikan sebagai atribut HTML, tetapi setCustomValidity berbeda karena memerlukan pemanggilan metode
  • Sebagai referensi tentang perbedaan atribut dan properti DOM, ditautkan Attributes vs Properties

Titik yang Membuat setCustomValidity Merepotkan

  • setCustomValidity hanya 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, setCustomValidity harus 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 onChange dan 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, dan onChange terasa berlebihan saat jumlah input bertambah, dan makin membingungkan ketika hanya sebagian input yang memakai customValidity
  • Kompleksitas seperti ini menonjol saat menangani API imperatif murni di komponen deklaratif
  • CustomValidity tidak 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-validity yang dapat ditentukan langsung pada input
  • Dalam framework deklaratif, atribut ini memungkinkan status validasi ditempatkan dekat dengan elemen input
  • Saat ini, HTML Spec tidak memiliki custom-validity sungguhan
  • 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 required untuk mencegah input kosong, lalu menjadikan input invalid dengan customValidity sesuai status loading dan hasil respons
  • Implementasinya terdiri dari dua bagian
    • Mengelola status request pemeriksaan keunikan nama pengguna dengan useQuery dari react-query
    • Menggunakan komponen <Input /> kustom yang dapat menerima prop customValidity
  • 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

  • setCustomValidity adalah 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-validity membuat validasi form native dapat digunakan dengan lebih natural
  • Fitur seperti ini dapat diharapkan suatu hari masuk secara native ke spesifikasi HTML

1 komentar

 
GN⁺ 2024-10-29
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 ini
    Chrome 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”

    • Masalahnya di sini bukan semata tidak adanya styling kustom, melainkan bahwa membaca status validity native dari input lalu merendernya sesuka kita sebenarnya cukup mudah
      Masalah 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() atau input.value = '...', event-event ini tidak dipicu
      Menurut saya ini topik yang bagus untuk diteliti terpisah dan diajukan ke platform web
    • Saya tidak begitu paham mengapa orang ingin menata hal seperti ini. Mengekspresikannya lewat warna atau layout memang bagus, tetapi pada titik tertentu kegunaan lebih penting daripada branding
    • Benar. Namun pesan default bisa disembunyikan dan diganti dengan pesan buatan sendiri. Meski begitu, manfaat validasi form tetap bisa didapat
    • Dalam hal itu, styling select tunggal juga tidak semudah yang semestinya
  • Hal terbesar, mudah diimplementasikan, tetapi kurang digunakan adalah memakai nilai atribut type yang spesifik seperti email, number, dan url
    Di mobile, ini bisa menampilkan keyboard yang optimal dan sangat meningkatkan pengalaman pengguna

    • Saya menghindari type=number dan memakai type=text inputmode=numeric sebagai gantinya. Tidak ada tombol panah yang tidak dibutuhkan kebanyakan pengguna untuk input angka, dan keyboard di iOS juga lebih baik
    • Mengejutkan betapa banyak tempat yang menampilkan “email” di form login tetapi tidak menetapkan type yang benar
    • Saya juga sangat sering melihat hal seperti ini pada input date. Setiap library frontend punya widget tanggalnya sendiri, dan banyak yang tampil buruk di layar kecil
      Demi 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
    • Sayangnya, input number masih kurang memadai dan tidak konsisten antarperamban. Pada akhirnya saya selalu kembali ke validasi JavaScript
    • Baru-baru ini saya harus memastikan untuk tidak memakai elemen input yang lebih kompleks. Alasannya, karena karena beberapa hal, input harus dikendalikan dengan keyboard layar yang diimplementasikan di dalam halaman
      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 customValidity bersifat 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 buruk

    • Memang ironis bahwa fitur yang disediakan browser bisa tidak lolos persyaratan aksesibilitas. Selama ini kita selalu diajari bahwa salah satu alasan memakai platform dan mengikuti elemen semantik adalah aksesibilitas
      Meski begitu, menurut saya mekanisme validasi native tetap layak dimanfaatkan. Untuk pesan error, kita tidak harus memakai tooltip validitas native; kita bisa membaca ValidityState dari input.validity secara langsung, lalu merender pesan dengan cara yang diinginkan dan menampilkan beberapa error bila perlu
      Browser 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 HiddenValidationInput yang 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 digunakan
      Saya sepenuhnya setuju bahwa API customValidity bersifat imperatif dan merepotkan. Saya menulis artikel yang membahas tepat bagian itu, dan saya berharap bagian ini juga membaik seiring waktu
    • Menarik, tetapi di aplikasi saya, saya mengimplementasikan semua validasi seperti itu. Validasi seluruh form ditangani di server. Pada dasarnya pendekatannya adalah punya beberapa lapisan validasi dan memanfaatkan semua yang mungkin
      Desain 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
    • Saya penasaran apa alasan para auditor menilainya seperti itu
  • 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

    • Benar. Bahkan hal yang relatif umum seperti referensi silang dengan field lain saja hampir langsung membutuhkan JS. Misalnya, jika pengguna sudah menentukan negara, kita bisa memvalidasi kode pos yang baru dimasukkan, tetapi kalau belum, kita harus menunggu pilihan negara
      Begitu mulai memakai JS, jauh lebih mudah mengerjakan semuanya dengan kode daripada mengutak-atik atribut validasi
    • Benar. Ini bagus sampai titik ketika kita membutuhkan date picker lintas-browser, tetapi sejak saat itu banyak hal harus diimplementasikan sendiri. Menyebalkan bahwa setelah bertahun-tahun selama ini, form HTML masih seprimitif ini
    • Sebagian besar fitur form berasal dari awal 90-an. Bisa jadi kita bahkan tidak bekerja pada milenium yang sama dengan sebagian penulis spesifikasinya
  • Kalau checkbox punya label, saya berharap label diberi atribut for supaya checkbox bisa dinyalakan dan dimatikan dengan mengeklik label. Secara pribadi ini salah satu hal yang paling mengganggu saya, meski mungkin cuma saya saja

    • Membungkus input dengan label juga berfungsi. Saya tidak terlalu paham kenapa orang cenderung memisahkan keduanya
      Dan 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
    • Bukan hanya Anda. Di situs aksesibel yang mengikuti ADA/WCAG, itu adalah fungsi yang diperlukan
  • Contoh sederhana yang tidak memakai React ada di sini
    https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectE...

    • Saya merasa sangat aneh ketika membahas validasi HTML standar tetapi semua contohnya ditampilkan dengan React. Kalau ingin mengajarkan cara kerja standar, React tidak boleh diasumsikan sebagai default
    • Sekarang sepertinya sebagian besar bisa dilakukan hanya dengan CSS. Mungkin JavaScript hanya diperlukan untuk mencegah popup bawaan muncul
  • 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

    • Saya menggunakan Firefox di semua perangkat mobile, tetapi sulit menyalahkan para developer yang mengabaikan Firefox untuk Android dalam masalah ini. Ini API yang sudah dijalankan oleh semua browser lain sejak 10 tahun lalu, dan tanggung jawab untuk membereskan kekacauan ini ada pada tim Firefox
      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
    • Firefox untuk Android punya basis pengguna lebih kecil daripada Samsung Internet atau Opera, sekitar 0,5%. Menghabiskan waktu untuk mendukungnya hampir seperti pemborosan
      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
    • Sebagai orang yang memakai Firefox setiap hari di Android, hal yang paling menyakitkan adalah tidak mampu mengejar standar. Terutama masalah terkait WebGL seperti [1] dan ketidaknyamanan kecil seperti [2]
      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
    • Ini jauh lebih buruk daripada “tidak berfungsi”. Validasi berjalan, tetapi sama sekali tidak menampilkan error
      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
    • Saya baru menyadarinya setelah baru-baru ini beralih ke Firefox di Android. Saat membuat aplikasi, ketika mencoba mengirim field wajib yang kosong, secara harfiah tidak ada apa pun yang terlihat
      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
    \w adalah karakter kata, \b adalah batas kata, \s adalah spasi, dan tanda baca sama sekali tidak diizinkan secara literal

    • Menurut saya ini bukan masalah yang khusus pada validasi HTML. Hal yang sama bisa terjadi pada jenis validasi apa pun. Aturan yang sama mungkin saja diterapkan di server, dan kalau begitu tidak ada harapan
    • Ini juga menjadi dasar yang bagus untuk poin yang ingin saya sampaikan. Atribut validitas bawaan seperti pattern memang keren, tetapi tidak cukup
      Misalnya, contoh “masukkan ulang kata sandi” bisa diimplementasikan dengan atribut pattern tanpa setCustomValidity. Untuk itu, regex harus disusun secara dinamis dari nilai input pertama
      Saya 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 besar
      Batasan “minimal 15 kata” juga bisa diekspresikan dengan jauh lebih enak dibaca, seperti value.split(/\s+/).length >= 15
  • Ada 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

    • Bagaimanapun juga, validasi backend tetap harus diimplementasikan sendiri. Selalu ada orang yang mencoba mengutak-atik form lewat browser aneh, curl, atau alat lain yang tidak punya validasi form bawaan yang sama
      Karena 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
    • Setuju. Saya berusaha memakai fitur standar browser jika memungkinkan, tetapi validasi bawaan tidak pernah terasa layak setelah dipertimbangkan
      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 sembarangan
  • Salah 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

    • Ada pseudo-class :user-invalid yang membantu menghindari ini sampai batas tertentu. Namun ada bagian yang kurang fleksibel, jadi mungkin tidak cukup tergantung use case
      https://developer.mozilla.org/en-US/docs/Web/CSS/:user-inval...
    • Ini salah satu alasan utama orang mulai memakai JavaScript: agar error hanya ditampilkan setelah form “disentuh” atau tepat sebelum submit
    • Saya tidak pernah memahami mengapa ini dipilih sebagai pengalaman default. Sebagian besar pengguna tidak suka semua elemen form berteriak kepada mereka padahal mereka belum sempat melakukan apa pun
      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