Setiap toko daring yang pernah kita pakai menawarkan alur interaksi yang sama: kotak pencarian di bagian atas, daftar hasil yang menyusut seiring kita mengetik, pilihan kategori, dan tombol pengurutan harga. Alur ini terlihat mudah, tetapi di baliknya ada banyak keputusan teknis — bagaimana kata “JavaScript” tetap menemukan hasil meski pengguna menulis “javascript”, kapan daftar dirender ulang, dan bagaimana pencarian, filter, serta pengurutan bekerja bersama tanpa saling menimpa. Proyek ini merangkai bekal course JavaScript menjadi satu produk utuh: katalog buku dengan pencarian teks, filter kategori dan harga, pengurutan, serta interaksi yang halus.
Pencarian dan filter adalah latihan proyek yang tepat karena tiga alasan. Pertama, masalahnya familier: kita semua tahu rasa mengetik di kotak pencarian, sehingga fokus tetap pada kode, bukan pada penjelasan domain. Kedua, logikanya berlapis tetapi tetap kecil — satu fungsi pencarian, dua fungsi filter, satu fungsi pengurutan, dan satu pipeline yang menyambungkannya; setiap lapisan cukup sederhana untuk dibedah tuntas. Ketiga, pengerjaannya menyentuh hampir semua materi course: data disusun sebagai array of objects, logika ditulis sebagai fungsi murni dalam module, hasil dirender ke DOM lewat DocumentFragment, input dikendalikan lewat event dan debounce, dan setiap perilaku dikunci oleh uji otomatis.
Pendekatan utama proyek ini sama dengan proyek kalkulator sebelumnya: pemisahan logika dari tampilan. Dataset dan seluruh logika pencarian hidup di module murni src/data.mjs dan src/daftar.mjs — tanpa satu pun document — sehingga bisa diuji langsung dengan Node. Berkas HTML hanya bertugas menampilkan hasil dan meneruskan perubahan input ke logika.
Proyek dikerjakan dalam empat bagian berurutan:
- Bagian 1: Mengelola Data — menyusun dataset 22 buku fiktif (
judul,penulis,kategori,harga,rating) disrc/data.mjs, merancang state pencarian (kata kunci, kategori aktif, urutan) sebagai fungsi murni disrc/daftar.mjs, dan menjalankan uji pertama dengannode --test. Kita belajar menjaga data dan state tetap imutabel sejak awal. - Bagian 2: Pencarian Teks — menormalisasi teks (huruf kecil, tanpa tanda baca, spasi rapat) dan menulis
cari(data, kataKunci)dengan aturan multi kata: semua kata harus ada. Kita belajar menangani kasus tepi — kata kunci kosong, spasi berlebih, hasil kosong — dengan uji yang eksplisit. - Bagian 3: Filter dan Sorting — menambah filter kategori, filter rentang harga, dan enam pilihan urutan dalam satu peta komparator (
localeCompareuntuk judul, selisih angka untuk harga dan rating), lalu menggabungkan semuanya dalam satu pipelineprosesDaftar. Kita belajar menyusun alur data satu arah dari pencarian sampai pengurutan. - Bagian 4: Optimasi Interaksi — menunda render dengan debounce, menghitung jumlah render sebagai ukuran keberhasilan, dan merender daftar hasil dalam satu batch lewat
DocumentFragment. Antarmuka lengkap dengan panel kontrol teruji lewat jsdom, sementara logika tetap teruji di terminal.
Struktur Berkas Proyek #
Seluruh kode proyek tinggal di tiga folder pada content/javascript/proyek/pencarian-dan-filter/:
pencarian-dan-filter/
├── src/
│ ├── data.mjs # dataset katalog: 22 buku fiktif
│ ├── daftar.mjs # logika murni: state, cari, filter, urutkan, debounce
│ └── index.html.txt # contoh antarmuka (salin ulang sebagai index.html)
├── styles/
│ └── styles.css # tampilan katalog
└── tests/
└── daftar.test.mjs # uji otomatis untuk daftar.mjsPeran tiap berkas:
| Berkas | Peran |
|---|---|
src/data.mjs | Sumber data: array 22 objek buku dengan lima properti. Tidak berubah sepanjang proyek. |
src/daftar.mjs | Otak pencarian: state, normalisasi teks, cari, filter, urutkan, pipeline, dan debounce. Tidak menyentuh DOM sama sekali. |
src/index.html.txt | Kerangka antarmuka: panel kontrol, baris status jumlah hasil, dan daftar kartu buku. |
styles/styles.css | Tampilan: panel kontrol dua kolom dan kartu buku berbentuk daftar. |
tests/daftar.test.mjs | Uji otomatis untuk seluruh fungsi logika, dijalankan test runner bawaan Node. |
Setiap bagian menampilkan kondisi berkas pada akhir bagian tersebut: blok kode yang membahas sebuah berkas menyediakan tautan unduhan salinannya di header blok. Versi lengkap dan terakhir dari seluruh berkas selalu ada di folder src/, styles/, dan tests/ di atas — jika Anda mengikuti proyek dari Bagian 1, berkas-berkas itu tumbuh bertahap mengikuti pembahasan.
Menjalankan Pengujian #
Uji otomatis dijalankan dari folder proyek dengan test runner bawaan Node.js:
cd pencarian-dan-filter
node --testNode mencari berkas uji dengan pola nama seperti *.test.mjs, menemukan tests/daftar.test.mjs, lalu menjalankan seluruh ujinya. Ringkasan di ujung keluarannya pada akhir proyek berbunyi:
ℹ tests 26
ℹ suites 10
ℹ pass 26
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 400.898821Angka pass 26 berarti seluruh uji lulus; duration_ms akan berbeda-beda di mesin Anda karena mengukur waktu eksekusi — tiga uji debounce di akhir menyumbang sekitar 320 milidetik karena sengaja menunggu timer sungguhan. Bila ingin menyasar satu berkas uji secara eksplisit, gunakan node --test tests/daftar.test.mjs.
Mulailah dari Bagian 1 dan ikuti urutannya — setiap bagian dibangun di atas bagian sebelumnya, dan pada bagian akhir katalog buku siap dipakai: pencarian yang sabar terhadap ketikan, filter yang bisa dipadukan, pengurutan yang konsisten, dan render yang tetap ringan.
Menyiapkan dataset dan state pencarian yang menjadi sumber kebenaran.
Buka → # Bagian 2: Pencarian TeksMencari data berdasarkan kata kunci dengan normalisasi teks.
Buka → # Bagian 3: Filter dan SortingMenggabungkan filter kategori dan pengurutan hasil.
Buka → # Bagian 4: Optimasi InteraksiDebounce input dan render efisien agar antarmuka tetap responsif.
Buka →Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
styles/styles.css
Unduh berkas/* styles.css — tampilan katalog buku pencarian dan filter */
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 24px 16px;
background-color: #f3f4f6;
color: #1f2937;
font-family: system-ui, sans-serif;
}
.katalog {
max-width: 640px;
margin: 0 auto;
}
h1 {
margin: 0 0 16px;
font-size: 1.6rem;
}
.panel-kontrol {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12px;
padding: 16px;
background-color: #ffffff;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
.panel-kontrol label {
display: flex;
flex-direction: column;
gap: 4px;
font-size: 0.85rem;
font-weight: 600;
}
.panel-kontrol input,
.panel-kontrol select {
padding: 8px 10px;
font-size: 1rem;
border: 1px solid #d1d5db;
border-radius: 8px;
background-color: #ffffff;
font-family: inherit;
}
.kolom-cari {
grid-column: span 2;
}
.status {
margin: 16px 0 8px;
font-size: 0.9rem;
color: #6b7280;
}
.hasil {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 10px;
}
.kartu-buku {
padding: 12px 16px;
background-color: #ffffff;
border-radius: 10px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
}
.kartu-buku h2 {
margin: 0 0 4px;
font-size: 1.05rem;
}
.kartu-buku .keterangan {
margin: 0 0 6px;
font-size: 0.85rem;
color: #6b7280;
}
.kartu-buku .harga {
margin: 0;
font-weight: 700;
color: #047857;
}