Di sebuah situs klien, sebuah platform booking, ada form registrasi pemilik properti. Salah satu field-nya nomor telepon, dan saya pasang validasi ringan lewat atribut pattern HTML5 supaya orang tidak mengetik huruf ngawur di kolom telepon. Di Firefox jalan. Di lokal jalan. Tapi di Chrome, field telepon itu seperti tidak punya validasi sama sekali: apa pun yang saya ketik lolos, dan tombol submit tidak pernah menahan input yang jelas-jelas salah.
Yang bikin curiga, ini bukan soal regex-nya kurang ketat. Kalau pattern di-parse tapi terlalu longgar, itu masih perilaku yang bisa dijelaskan. Ini beda: seolah-olah browser membuang atribut pattern itu bulat-bulat. Dan memang begitulah yang terjadi. Ketika sebuah pattern tidak bisa di-compile jadi regex yang valid, spec HTML bilang constraint-nya diabaikan, jadi field-nya dianggap selalu valid. Validasi rusak, tanpa pesan error apa pun yang kelihatan di UI.
Jalan buntu yang saya coba
Refleks pertama saya: pasti regex-nya salah tulis. Pattern awal saya kira-kira begini:
<input id="register-phone" pattern="[0-9 +().-]*">Saya pikir mungkin tanda - di tengah character class ditafsirkan sebagai range. Jadi saya pindahkan ke depan dan coba beberapa varian lain:
<!-- masih gagal di Chrome -->
<input pattern="[-0-9 +().]">
<input pattern="[0-9 +().\-]*">Semua masih gagal. Memindahkan - ke ujung memang praktik yang benar untuk menghindari range yang tidak disengaja, tapi di Chrome tidak ada bedanya. Field-nya tetap seperti tidak tervalidasi. Di titik ini saya sadar saya cuma menebak-nebak, dan menebak regex itu buang waktu. Saya perlu melihat error yang sebenarnya.
Menemukan error yang sesungguhnya
Karena pattern di bawah kap adalah regex, saya coba compile string pattern itu langsung di console persis seperti browser modern melakukannya. Chrome sekarang memvalidasi pattern dengan flag v, jadi saya tiru:
new RegExp(document.querySelector('#register-phone').pattern, 'v');
// Uncaught SyntaxError: Invalid regular expressionDi situ akhirnya keluar suaranya: SyntaxError. Bukan regex yang longgar, tapi regex yang sama sekali tidak bisa di-compile. Constraint yang tidak bisa di-compile itulah yang diam-diam dibuang, dan itu sebabnya field-nya lolos apa saja.
Akar masalahnya: flag v
Ini bagian yang mengejutkan. Chrome kini meng-compile pattern HTML5 dengan flag v (mode unicodeSets) dari ECMAScript 2024, bukan lagi mode regex biasa. Flag v memperkenalkan aturan character class yang jauh lebih ketat. Salah satu aturannya: sederet karakter tanda baca tertentu diperlakukan sebagai "double punctuator" yang direservasi di dalam character class, dan ( termasuk yang harus di-escape. Di mode lama, ( di dalam sebuah character class diperlakukan sebagai karakter literal biasa dan tidak masalah. Di mode v, ( yang tidak di-escape di dalam character class adalah syntax error.
Jadi semua tebakan saya soal - itu salah sasaran. Yang bikin Chrome menolak bukan tanda minus, tapi tanda kurung ( dan ) yang saya biarkan telanjang di dalam class. Firefox dan build Chrome yang lebih lama masih memakai parser regex yang lama, makanya pattern yang sama lolos di sana. Begitu Chrome pindah ke v, character class yang dulu sah jadi ilegal.
Perbaikannya
Solusinya bukan mengakali urutan karakter, tapi meng-escape setiap karakter khusus secara eksplisit sehingga valid di mode v:
<input id="register-phone" pattern="[\d\s+\(\)\.\-]*">\d untuk digit, \s untuk spasi, lalu \(, \), \., dan \- untuk kurung, titik, dan minus yang di-escape semua. Saya verifikasi langsung dengan cara yang sama yang tadi memberi tahu saya penyebabnya:
new RegExp(document.querySelector('#register-phone').pattern, 'v');
// tidak melempar apa punTidak ada SyntaxError. Field-nya kembali memvalidasi: menerima format nomor telepon lokal yang wajar dan menolak input seperti abc. Dari sisi kode, perubahannya cuma beberapa backslash, tapi tanpa tahu soal flag v, backslash itu terlihat berlebihan dan gampang dianggap tidak perlu.
Pelajaran
- Kalau
patternHTML5 seakan "diabaikan" di satu browser, jangan asumsikan regex-nya longgar; bisa jadi regex-nya gagal di-compile dan constraint-nya dibuang diam-diam. - Reproduksi dengan
new RegExp(el.pattern, 'v')di console untuk memaksa munculnyaSyntaxErroryang sebenarnya, alih-alih menebak. - Chrome memvalidasi
patterndengan flagvECMAScript 2024; character class modevjauh lebih ketat dari mode lama. - Di mode
v,(dan sederet tanda baca lain adalah karakter reservasi di dalam character class dan wajib di-escape. - Escape eksplisit itu portable:
[\d\s+\(\)\.\-]*valid di parser lama maupunv, jadi aman lintas browser. - Beda perilaku antar-browser pada
patternbiasanya soal versi parser regex, bukan bug di logika form kamu.
