Lompat ke konten
dokumentasi.id
Commit 314576d
v1
Star2 rb

5

Beranda dan Pencarian Klien

Beranda freeStuffDev adalah satu halaman statis yang dibangun dari Content Collection tools, lalu dilengkapi pencarian yang berjalan sepenuhnya di browser. Tidak ada request pencarian ke server: penyaringan kartu dilakukan dengan menambah atau melepas kelas hidden pada elemen yang sudah tercetak di DOM.

Alur render beranda

Halaman mulai dengan getCollection('tools', ...) yang menyaring entri memakai predikat data.isPaid !== true. Karena predikatnya !== true dan bukan === false, entri yang tidak menuliskan field isPaid sama sekali tetap ikut tampil. src/pages/index.astro:8-14

Sebelum dirender, larik diacak dengan tools.sort(() => Math.random() - 0.5). Pengacakan itu berjalan di sisi server saat halaman dibangun; pada keluaran build statis urutan hasil acak ikut tercetak ke HTML, sehingga memuat ulang beranda tidak mengubah urutan kartu. src/pages/index.astro:8-14

graph LR
    A["getCollection('tools')"] --> B["filter data.isPaid !== true"]
    B --> C["tools.sort(Math.random() - 0.5)"]
    C --> D["tools.map(Card.astro)"]
    D --> E["document.querySelectorAll('.cards')"]
    E --> F["liveSearch pada keyup"]
    style B fill:#2d7d46,color:#fff
    style C fill:#8b5cf6,color:#fff
    style F fill:#f59e0b,color:#000
%% fig: Jalur data dari Content Collection ke pencarian klien
%% desc: Collection disaring isPaid, urutannya diacak saat build, dirender jadi kartu .cards, lalu disaring lagi di browser oleh liveSearch.
Fig. 1: Jalur data dari Content Collection ke pencarian klienCollection disaring isPaid, urutannya diacak saat build, dirender jadi kartu .cards, lalu disaring lagi di browser oleh liveSearch.

Kartu dirender di dalam satu div dengan tools.map((tool) => <Card tool={tool} />), tanpa prop tambahan. src/pages/index.astro:69-75 Card.astro menaruh kelas cards pada elemen akar, menerima isHidden dengan nilai bawaan false, dan menyisipkan input type="hidden" name="tool-slug". src/components/Card.astro:3 src/components/Card.astro:14-16

Di atas daftar itu halaman memasang Header berisi judul dan deskripsi yang sama dengan meta halaman, lalu <Sponsors />. src/pages/index.astro:21-33 Sponsors.astro pada commit ini hanya menampilkan <details> berisi ajakan menjadi sponsor; grid banner sponsor sudah ditulis tetapi dinonaktifkan sebagai komentar. src/components/Sponsors.astro:1-13 src/components/Sponsors.astro:19-33

Filter isPaid dan badge yang tidak terpakai

Penyaringan isPaid terjadi di index.astro, jadi kartu tool berbayar tidak pernah mencapai browser. Card.astro sendiri masih memuat cabang yang mencetak badge Paid Tool ketika tool.data.isPaid bernilai benar, tetapi cabang itu tidak pernah aktif di beranda. src/components/Card.astro:18-25 Badge tersebut baru terlihat di halaman yang merender seluruh koleksi, yaitu halaman favorit yang memakai isHidden untuk menyembunyikan kartu; lihat Fitur Favorit di Local Storage.

Kotak pencarian dan kontrol Sort by

Markup kontrol terdiri dari input#searchbox dengan placeholder search anything here... dan select#sort berisi opsi random serta newest. src/pages/index.astro:36-42 src/pages/index.astro:46-52

KontrolNilaiPerilakuRujukan
input#searchboxteks apa punmemicu keyup, lalu liveSearch setelah jedasrc/pages/index.astro:86-89
select#sortrandomlocation.href = '/'src/pages/index.astro:54-65
select#sortnewestlocation.href = '/recent'src/pages/index.astro:54-65

Handler change pada #sort hanya memindahkan halaman; ia tidak menyusun ulang kartu di tempat. Opsi newest mengarah ke /recent, dan urutan di halaman itu dijelaskan di Halaman Recent dan Pengurutan. src/pages/index.astro:54-65

liveSearch membaca textContent

initSearch didefinisikan pada skrip inline di akhir halaman. Komentar di kode menandai penempatan skrip setelah kontainer kartu, dan daftar kartu memang baru diambil saat initSearch dipanggil, bukan saat skrip diurai. src/pages/index.astro:77-79

Bagian skripNilaiRujukan
koleksi kartudocument.querySelectorAll('.cards')src/pages/index.astro:80
jeda debouncetypingTimer, typeInterval = 500src/pages/index.astro:82-84
event pemicukeyup pada #searchboxsrc/pages/index.astro:86-89
perbandingancards[i].textContent.toLowerCase().includes(searchQ.toLowerCase())src/pages/index.astro:91-95
kelas toggleremove("hidden") dan add("hidden")src/pages/index.astro:96-98
titik mountDOMContentLoaded, astro:after-swapsrc/pages/index.astro:104-105

Setiap keyup membatalkan timeout sebelumnya lewat clearTimeout(typingTimer) dan memasang setTimeout(liveSearch, 500), jadi penyaringan berjalan setengah detik setelah ketikan terakhir. src/pages/index.astro:82-84 Karena yang dibandingkan adalah textContent seluruh kartu, semua teks yang tercetak di dalam kartu ikut menjadi bahan pencarian.

Bagian kartuSumber di Card.astroMasuk textContent
judul, snippet, tagtool.data.title, tool.data.snippet, tool.data.tags.join(', ')ya
isi penawarantool.body di dalam Free Offer:ya
label tombolSave, Edit, Visit, Alternativesya
slugatribut value pada input name="tool-slug"tidak, value bukan textContent

⚠️ Label tombol dan teks Free Offer: berada di dalam kartu, sehingga query seperti save, visit, atau free cocok untuk semua kartu sekaligus. Query kosong juga tidak menyembunyikan apa pun: includes('') selalu true, jadi saat kotak dikosongkan seluruh kartu kembali tampil. src/pages/index.astro:91-95 Selain itu searchInput diambil tanpa pemeriksaan null lalu langsung dipanggil addEventListener, sehingga menghapus #searchbox dari markup akan membuat initSearch gagal. src/pages/index.astro:82-84 src/pages/index.astro:86-89

Perpindahan kartu antara tampil dan hidden

Satu-satunya status visual kartu adalah kehadiran kelas hidden, dan dua tempat di repository mengubahnya.

stateDiagram-v2
    [*] --> Visible
    Visible --> Hidden: "textContent tidak memuat searchQ"
    Hidden --> Visible: "textContent memuat searchQ"
    Visible --> Hidden: "unsave menambah hidden pada .cards"
    Hidden --> [*]
    style Visible fill:#2d7d46,color:#fff
    style Hidden fill:#dc2626,color:#fff

%% fig: Siklus hidup kartu beranda %% desc: liveSearch memindahkan kartu antara Visible dan Hidden berdasarkan textContent, sedangkan unsave memakai kelas yang sama di halaman favorit.

Transisi pertama dan kedua berasal dari liveSearch: kartu yang tidak cocok mendapat kelas hidden, kartu yang cocok melepasnya. src/pages/index.astro:96-98 Transisi ketiga berasal dari unsave di layout, yang menambahkan hidden pada .cards terdekat di halaman favorit. src/layouts/Layout.astro:86-87

initSearch dipasang pada DOMContentLoaded dan astro:after-swap karena layout memakai ViewTransitions; tanpa pemasangan ulang itu, pencarian berhenti bekerja setelah navigasi internal. src/pages/index.astro:104-105 Detail mekanisme layout dan transisi halaman ada di Layout, Sidebar, dan Tema.