Active Search — HTMX

Active search — hasil pencarian yang update saat user mengetik. Pattern 🔍 Key Features keyup changed — hanya trigger jika value berubah delay:300ms — debounce…

Active search adalah kolom cari yang menampilkan hasil sambil kamu mengetik, tanpa tombol Cari. Namanya macam-macam: active search, live search, instant search, semuanya pola yang sama. Bayangin kasir Laundry Bu Rini mencari nama di buku order. Dia menunggu pelanggan selesai menyebut nama, baru membalik buku. Kalau pelanggan meralat di tengah jalan ("Wu... eh, Wulan"), kasir yang pintar berhenti mencari "Wu" dan langsung mencari "Wulan". Kalau pelanggan bilang "gak jadi", halaman yang terbuka ditutup lagi.

🧪 Tebakan cepat: Kolom cari di papan memakai hx-trigger="keyup changed delay:500ms". Hasil untuk "wulan" sudah tampil, lalu kamu mengklik tanda × di ujung kolom. Kolomnya kosong. Apa yang terjadi pada daftar hasilnya? Jawabannya ada di bagian "Tombol × dan event search".

Polanya: satu kolom, satu daftar hasil

<input id="cari" name="cari" type="search" placeholder="Nama pelanggan"
       hx-get="/cari"
       hx-trigger="keyup changed delay:500ms, search"
       hx-target="#hasil"
       hx-indicator="#mencari"
       hx-sync="this:replace">
<span id="mencari" class="htmx-indicator">Mencari...</span>
<ul id="hasil"></ul>

Server membalas baris-baris <li> saja, bukan halaman utuh:

<li>#8 Mbak Wulandari, 2 kg: Sudah diambil</li>
<li>#13 Mbak Wulan, 2 kg: Disetrika</li>

Mengetik "wulan": satu request

Dari pelajaran hx-trigger: keyup tanpa apa-apa mengirim satu request per huruf. delay:500ms menunggu 500 ms tanpa ketikan, dan changed mengabaikan tombol yang gak mengubah nilai (Shift, panah). Mengetik wulan dengan jeda 120 ms per huruf:

Waktu Kejadian
0 sampai 480 ms w, u, l, a, n: tiap huruf mengulang hitungan 500 ms
980 ms GET /cari?cari=wulan, satu-satunya request
1.130 ms balasan tiba, 2 baris masuk ke #hasil

Tombol × dan event search

Kolom type="search" di Chrome, Edge, dan Safari punya tanda × untuk mengosongkannya. Mengklik × gak memicu keyup, karena gak ada tombol keyboard yang dilepas. Jadi kolomnya kosong, tapi daftar hasil "wulan" tetap tampil. Itu jawaban tebakannya: daftar basi, dan pengguna bingung.

Browser yang sama mengirim event search saat × diklik dan saat Enter ditekan. Tambahkan sebagai pemicu kedua:

hx-trigger="keyup changed delay:500ms, search"

Balasan basi dan hx-sync

Misalnya server butuh 1.200 ms untuk wu (kata pendek, banyak yang dicocokkan) dan 150 ms untuk wulan. Kamu mengetik wu, berhenti 0,7 detik, lalu meneruskan lan:

Waktu Tanpa hx-sync Dengan hx-sync="this:replace"
620 ms GET /cari?cari=wu dikirim sama
700 sampai 940 ms l, a, n diketik sama
1.440 ms wulan siap dikirim, tapi wu belum dibalas: wulan antre wu dibatalkan, GET /cari?cari=wulan langsung dikirim
1.590 ms kolom berisi "wulan", daftar masih kosong hasil wulan tampil, selesai
1.820 ms hasil wu yang basi tampil (3 baris), baru wulan dikirim
1.970 ms hasil wulan tampil

Isi hx-sync adalah elemen yang dijadikan patokan, titik dua, lalu strateginya:

Strategi Kalau sudah ada request berjalan
replace yang lama dibatalkan, yang baru dikirim. Pilihan untuk pencarian
drop yang baru dibuang. Ini bawaan kalau strateginya gak ditulis
abort yang baru dibuang, sama seperti drop. Bedanya, request ini dibatalkan kalau elemen patokannya (misalnya form) mengirim request lain
queue last yang baru antre, cuma yang terakhir yang disimpan

hx-sync="this" tanpa strategi berarti drop: wulan dibuang, dan daftar berhenti di hasil wu.

throttle: kenapa gak cocok untuk pencarian

hx-trigger="keyup changed throttle:1s" mengirim huruf pertama langsung, lalu membuang ketikan lain selama 1 detik. Mengetik wulan dalam 0,5 detik cuma mengirim cari=w, dan wulan gak pernah terkirim kalau kamu berhenti mengetik. throttle membatasi laju, delay menunggu kamu selesai. Pencarian pakai delay. Latihan kedua memakai delay bersama dua ide dari pelajaran ini: event search dan hx-sync="this:replace".

⚠️ Jebakan yang sering ketemu

TL;DR: Active search (live search) mengirim kolom cari sambil diketik: hx-get="/cari", hx-target="#hasil", dan hx-trigger="keyup changed delay:500ms, search". keyup (atau input) memicu, changed mengabaikan tombol yang gak mengubah nilai, delay menunggu ketikan terakhir, dan search menangkap tombol × dan Enter di Chrome, Edge, dan Safari. Tanpa search, mengosongkan kolom lewat × membiarkan hasil lama tampil. hx-sync="this:replace" membatalkan request lama yang masih berjalan, jadi hasil basi gak pernah tampil. hx-sync="this" saja berarti drop dan membuang ketikan terakhir. throttle membuang ketikan terakhir juga, jadi untuk pencarian pakai delay.

Yang akan kamu pelajari