Masukkan Password
75 Cineflix: Cari Film
Lecture ini membahas fitur Search pada project Synaphix. Ini sangat bagus untuk memahami kombinasi beberapa konsep yang sudah kamu pelajari:
addEventListener().valuefilter()toLowerCase()includes()- callback function
true/falserenderAllMovies()
Inti besarnya adalah:
Ambil teks dari input → cari movie yang namanya mengandung teks tersebut → simpan hasilnya dalam array baru → render ulang UI menggunakan array hasil pencarian.
1. Gambaran fitur yang ingin dibuat
Di aplikasi ada search box:
Search movie:
[ enter ] [ Search ]
Misalnya user mengetik:
enter
Kemudian klik:
[ Search ]
Kita ingin mencari movie yang nama movie-nya mengandung "enter".
Misalnya:
Interstellar
Inception
Bad Boys
The Dark Knight
Maka hasilnya:
Interstellar
Karena:
Interstellar
↑
enter
Sedangkan Bad Boys tidak mengandung "enter".
2. Flow keseluruhan fitur Search
Secara sederhana:
User mengetik search text
↓
klik Search
↓
searchMovies()
↓
ambil input.value
↓
searchText
↓
movies.filter()
↓
cek setiap movie.name
↓
toLowerCase()
↓
includes()
↓
true / false
↓
searchedMovies[]
↓
renderAllMovies(searchedMovies)
↓
UI berubah
Jadi sebenarnya fitur ini adalah gabungan:
INPUT
+
FILTER
+
STRING SEARCH
+
RENDER
3. Step pertama: mengambil tombol Search
Misalnya HTML-nya:
<button id="search-movie-button">
Search
</button>
JavaScript:
let searchMovieButton =
document.getElementById("search-movie-button");
Artinya:
“Ambil element button dengan ID
search-movie-buttondari DOM.”
Sekarang:
searchMovieButton
↓
<button>Search</button>
4. Tambahkan event listener
Kemudian:
searchMovieButton.addEventListener(
"click",
searchMovies
);
Artinya:
“Ketika tombol Search diklik, jalankan function
searchMovies.”
Perhatikan:
searchMovies
bukan:
searchMovies()
Karena kita sedang memberikan function reference sebagai callback.
Jadi:
User klik button
↓
"click" event
↓
searchMovies()
5. Buat function searchMovies()
Sekarang:
function searchMovies() {
}
Function ini akan menjadi pusat dari seluruh logic search.
Di dalamnya kita akan melakukan beberapa langkah.
6. Langkah pertama: mengambil teks dari search box
Misalnya HTML:
<input
type="text"
id="search-movie-text"
>
Kita ambil element-nya:
let searchTextInput =
document.getElementById("search-movie-text");
Kemudian kita ambil .value:
let searchText = searchTextInput.value;
Atau langsung:
const searchText =
document.getElementById("search-movie-text").value;
Misalnya user mengetik:
enter
Maka:
searchText
berisi:
"enter"
7. Ingat konsep .value
Ini adalah konsep yang sudah kita pelajari ketika membaca form.
Untuk input:
<input id="name">
kita bisa:
let name = document.getElementById("name").value;
.value berarti:
ambil nilai yang sedang ada di dalam form control tersebut.
Misalnya:
Input:
┌─────────────────────┐
│ interstellar │
└─────────────────────┘
maka:
input.value
menghasilkan:
"interstellar"
8. Sekarang kita punya searchText
Misalnya:
const searchText = "enter";
Dan kita memiliki:
movies
yang berisi banyak object:
[
{
name: "Interstellar",
rating: 4.6
},
{
name: "Inception",
rating: 4.5
},
{
name: "Bad Boys",
rating: 3.2
}
]
Sekarang pertanyaannya:
Bagaimana kita mengambil hanya movie yang namanya mengandung
"enter"?
Jawabannya:
filter()
9. filter() adalah inti fitur Search
Kita sudah belajar bahwa:
filter()
digunakan untuk:
memilih elemen-elemen yang memenuhi suatu kondisi.
Contoh sederhana:
let numbers = [1, 2, 3, 4, 5];
let evenNumbers = numbers.filter((number) => {
return number % 2 === 0;
});
Hasil:
[2, 4]
Karena hanya angka yang memenuhi kondisi:
number % 2 === 0
yang masuk ke array baru.
10. Sekarang terapkan filter() pada movies
Kita bisa:
const searchedMovies = movies.filter((movie) => {
});
Di sini:
movies
adalah array asli.
Sedangkan:
searchedMovies
akan menjadi array baru yang hanya berisi movie yang cocok dengan search.
11. Apa isi parameter movie?
Ini callback:
(movie) => {
...
}
movie mewakili movie yang sedang diperiksa.
Misalnya:
movies
↓
┌───────────────┐
│ Interstellar │ ← movie
├───────────────┤
│ Inception │
├───────────────┤
│ Bad Boys │
└───────────────┘
Pada iteration pertama:
movie = Interstellar object
Iteration berikutnya:
movie = Inception object
Berikutnya:
movie = Bad Boys object
Dan seterusnya.
12. Kita ingin memeriksa movie.name
Setiap object movie memiliki:
movie.name
Misalnya:
movie.name
menghasilkan:
"Interstellar"
Sekarang kita ingin bertanya:
Apakah
"Interstellar"mengandung search text"enter"?
Untuk itu kita menggunakan:
includes()
13. includes() digunakan untuk mengecek apakah sesuatu ada di dalam String
Contoh:
"Interstellar".includes("enter")
hasilnya:
true
Karena "enter" memang ada di dalam "Interstellar".
Contoh lain:
"Bad Boys".includes("enter")
hasilnya:
false
Jadi:
includes()
menghasilkan Boolean:
true
atau
false
14. Kenapa toLowerCase() digunakan?
Ada masalah.
Misalnya movie:
Interstellar
Sedangkan user mengetik:
ENTER
Kalau kita langsung:
"Interstellar".includes("ENTER")
hasilnya:
false
Karena JavaScript membedakan huruf besar dan kecil.
"enter" ≠ "ENTER"
Ini disebut case-sensitive.
15. Solusinya: samakan semuanya menjadi lowercase
Movie name:
movie.name.toLowerCase()
Misalnya:
"Interstellar"
menjadi:
"interstellar"
Search text juga sebaiknya dibuat lowercase:
searchText.toLowerCase()
Misalnya:
"ENTER"
menjadi:
"enter"
Sekarang:
"interstellar".includes("enter")
hasil:
true
16. Ini bagian yang sangat penting
Kita ingin:
movie.name
diubah menjadi lowercase:
movie.name.toLowerCase()
Kemudian dicek:
.includes(searchText.toLowerCase())
Sehingga:
movie.name
↓
toLowerCase()
↓
includes(searchText)
↓
true / false
17. Function filter() lengkap
Maka:
const searchedMovies = movies.filter((movie) => {
return movie.name
.toLowerCase()
.includes(searchText.toLowerCase());
});
Ini sebenarnya sangat sederhana kalau dibaca dari dalam ke luar.
Bagian paling dalam:
movie.name
↓
movie.name.toLowerCase()
↓
movie.name.toLowerCase().includes(...)
↓
menghasilkan:
true / false
↓
filter() menggunakan Boolean tersebut untuk menentukan apakah movie masuk hasil.
18. Bagaimana filter() bekerja?
Misalnya:
searchText = "enter";
Movies:
Interstellar
Inception
Bad Boys
The Dark Knight
filter() mengecek satu per satu.
Movie 1
"interstellar".includes("enter")
hasil:
true
Maka:
Interstellar → MASUK
Movie 2
"inception".includes("enter")
hasil:
false
Maka:
Inception → TIDAK MASUK
Movie 3
"bad boys".includes("enter")
hasil:
false
Maka:
Bad Boys → TIDAK MASUK
Movie 4
"the dark knight".includes("enter")
hasil:
false
Maka:
The Dark Knight → TIDAK MASUK
Hasil akhirnya:
searchedMovies
menjadi:
[
{
name: "Interstellar",
...
}
]
19. Kenapa filter() membutuhkan true atau false?
Ini inti konsep filter().
Pattern:
array.filter((element) => {
return CONDITION;
});
Kalau:
CONDITION === true
→ element masuk array hasil.
Kalau:
CONDITION === false
→ element tidak masuk.
Jadi:
filter()
↓
┌───────┴───────┐
↓ ↓
true false
↓ ↓
MASUK TIDAK MASUK
hasil[] hasil[]
Dan includes() sangat cocok dengan filter() karena includes() memang menghasilkan Boolean.
20. Hubungan filter() + includes()
Ini adalah pola penting:
array.filter((item) => {
return item.includes(keyword);
});
Artinya:
Ambil semua item yang mengandung keyword.
Untuk project kita:
movies.filter((movie) => {
return movie.name.includes(searchText);
});
Karena movie.name adalah String.
21. Setelah mendapatkan searchedMovies
Sekarang kita sudah punya:
const searchedMovies = movies.filter(...);
Misalnya hasilnya:
searchedMovies
↓
[
Interstellar,
Inception
]
Tapi UI masih mungkin menampilkan:
Interstellar
Inception
Bad Boys
The Dark Knight
Kenapa?
Karena sekali lagi:
Data dan UI adalah dua hal yang berbeda.
22. Kita harus render ulang
Project sudah mempunyai function:
renderAllMovies(movieList)
Function ini bertugas mengubah array movie menjadi movie cards di UI.
Jadi:
renderAllMovies(searchedMovies);
Artinya:
“Render hanya movie yang ada di hasil pencarian.”
Flow:
movies
↓
filter()
↓
searchedMovies
↓
renderAllMovies()
↓
UI
23. Ingat renderAllMovies() harus clear UI
Sebelumnya kita sudah memperbaiki:
function renderAllMovies(movieList) {
movieListSection.innerHTML = "";
movieList.forEach((movie) => {
// create movie card
});
}
Ini penting.
Kalau tidak:
UI lama
+
hasil search
akan ditampilkan bersamaan.
Dengan:
movieListSection.innerHTML = "";
kita melakukan:
hapus UI lama
↓
render hasil search
24. Full function search
Kurang lebih menjadi:
function searchMovies() {
const searchText =
document.getElementById("search-movie-text").value;
const searchedMovies = movies.filter((movie) => {
return movie.name
.toLowerCase()
.includes(searchText.toLowerCase());
});
renderAllMovies(searchedMovies);
}
Dan event listener:
const searchMovieButton =
document.getElementById("search-movie-button");
searchMovieButton.addEventListener(
"click",
searchMovies
);
25. Mari baca code tersebut seperti manusia
Code:
const searchText =
document.getElementById("search-movie-text").value;
Baca:
“Ambil teks yang diketik user.”
Kemudian:
const searchedMovies = movies.filter((movie) => {
Baca:
“Dari semua movie, pilih movie yang memenuhi kondisi.”
Kemudian:
return movie.name
.toLowerCase()
.includes(searchText.toLowerCase());
Baca:
“Apakah nama movie, setelah dibuat lowercase, mengandung search text yang juga sudah dibuat lowercase?”
Kalau iya:
true
movie masuk.
Kalau tidak:
false
movie dibuang dari hasil.
Terakhir:
renderAllMovies(searchedMovies);
Baca:
“Tampilkan hasil pencarian ke UI.”
26. Kenapa search "in" bisa mendapatkan dua movie?
Misalnya:
Interstellar
Inception
User mengetik:
in
Kita melakukan:
"interstellar".includes("in")
→ true
dan:
"inception".includes("in")
→ true
Maka:
searchedMovies
↓
Interstellar
Inception
27. Search "ah"
Misalnya ada:
Bahubali
Interstellar
Inception
User mengetik:
ah
Maka:
"bahubali".includes("ah")
→ true
Sedangkan:
"interstellar".includes("ah")
→ false
Hasil:
Bahubali
28. Search "boys"
Kalau user mengetik:
boys
maka:
"bad boys".includes("boys")
→ true
Maka movie:
Bad Boys
masuk hasil.
29. Apa yang terjadi jika search box kosong?
Ini juga menarik.
Misalnya:
searchText = "";
Kemudian:
"interstellar".includes("")
hasilnya:
true
Begitu juga:
"bad boys".includes("")
→ true
dan:
"inception".includes("")
→ true
Jadi:
movies.filter(...)
akan mengembalikan semua movie.
Makanya ketika search box dikosongkan dan Search diklik:
semua movie muncul kembali
Tanpa perlu membuat if khusus.
30. Ini merupakan efek menarik dari includes("")
Secara sederhana:
"apa saja".includes("")
→ true
Karena string kosong dianggap ditemukan di dalam setiap string.
Maka:
searchText = ""
↓
semua movie lolos filter
↓
semua movie ditampilkan
Ini salah satu alasan kenapa implementasi lecture bisa sederhana.
31. filter() tidak mengubah movies
Ini juga penting.
Berbeda dengan:
sort()
yang mengubah array asli, filter() menghasilkan array baru.
Misalnya:
const searchedMovies = movies.filter(...);
Maka:
movies
↓
tetap berisi semua movie
searchedMovies
↓
berisi movie yang cocok
Visual:
movies
│
├── Interstellar
├── Inception
├── Bad Boys
└── Bahubali
│
↓ filter("in")
│
└──────────────→ searchedMovies
│
├── Interstellar
└── Inception
Jadi search tidak menghapus movie dari movies.
32. Ini berbeda dengan sort()
Ini perbedaan yang sangat penting:
| Method | Hasil | Mengubah array asli? |
|---|---|---|
sort() | array yang sudah diurutkan | ✅ Ya |
filter() | array baru berisi elemen yang cocok | ❌ Tidak |
map() | array baru hasil transformasi | ❌ Tidak |
forEach() | tidak membuat array hasil khusus | ❌ Tidak |
Dalam project:
movies.sort(...)
→ urutan movies berubah.
Sedangkan:
movies.filter(...)
→ movies tetap lengkap.
33. Ini alasan filter() cocok untuk Search
Bayangkan kita punya:
movies = semua movie
Kita tidak ingin menghapus movie yang tidak cocok.
Kita hanya ingin:
“Untuk tampilan sekarang, tunjukkan movie yang cocok.”
Maka:
const searchedMovies = movies.filter(...);
sangat cocok.
Data asli:
movies
tetap menjadi:
semua movie
Sedangkan tampilan menggunakan:
searchedMovies
34. Search sebenarnya bukan menghapus data
Ini mental model yang bagus:
❌ Salah:
"Search menghapus movie yang tidak cocok."
✅ Benar:
"Search membuat array hasil yang hanya berisi movie yang cocok,
lalu hasil tersebut ditampilkan."
Contoh:
movies
↓
[ A, B, C, D, E ]
filter("B")
↓
[ B ]
movies masih:
[ A, B, C, D, E ]
35. toLowerCase() tidak mengubah movie name
Misalnya:
movie.name = "Interstellar";
Ketika kita melakukan:
movie.name.toLowerCase()
hasilnya:
"interstellar"
Tetapi:
movie.name
tetap:
"Interstellar"
Karena String di JavaScript bersifat immutable.
Jadi kita hanya menggunakan hasil lowercase untuk pencarian.
36. Kenapa search text juga harus lowercase?
Misalnya:
Movie:
Interstellar
User:
ENTER
Kalau hanya movie yang lowercase:
"interstellar".includes("ENTER")
→ false
Tapi kalau keduanya:
"interstellar".includes("enter")
→ true
Maka:
movie.name.toLowerCase()
.includes(searchText.toLowerCase())
membuat pencarian menjadi case-insensitive.
Artinya:
enter
ENTER
Enter
eNtEr
semuanya dianggap sama.
37. Pipeline lengkap filter() + includes()
Perhatikan:
const searchedMovies = movies.filter((movie) => {
return movie.name
.toLowerCase()
.includes(searchText.toLowerCase());
});
Kita bisa membacanya:
movie
↓
movie.name
↓
toLowerCase()
↓
includes(searchText)
↓
true / false
↓
filter()
↓
masuk / tidak masuk
Ini pola yang sangat penting untuk dipahami.
38. Hubungan dengan map() yang sebelumnya dipelajari
Sebelumnya kamu belajar:
movie.genre.map(...)
map() digunakan untuk mengubah setiap element.
Contoh:
["action", "drama"]
↓ map()
["Action", "Drama"]
Sekarang:
movies.filter(...)
digunakan untuk memilih element.
Contoh:
[A, B, C, D]
↓ filter()
[A, C]
Jadi:
map()
→ "Saya ingin mengubah setiap item."
filter()
→ "Saya ingin memilih item tertentu."
39. Bedakan filter() dengan find()
Nanti ini mungkin akan membingungkan.
filter()
Bisa menghasilkan banyak item:
movies.filter(...)
hasil:
[
movie1,
movie2,
movie3
]
find()
Mencari satu item pertama yang cocok:
movies.find(...)
hasil:
movie1
Jadi untuk search movie:
User mengetik "in"
↓
mungkin ada 2, 3, 10 movie
↓
filter() cocok
40. includes() juga berbeda dengan filter()
Ini juga perlu dibedakan.
includes()
Menjawab:
“Apakah sesuatu ada?”
Hasil:
true
atau:
false
Contoh:
"Interstellar".includes("enter");
// true
filter()
Menjawab:
“Element mana saja yang memenuhi kondisi?”
Hasil:
Array
Contoh:
movies.filter(...)
menghasilkan:
[ ...movies yang cocok... ]
Jadi:
includes()
→ Boolean
filter()
→ Array baru
41. Kenapa includes() cocok dimasukkan ke filter()?
Karena:
filter() butuh:
true / false
includes() menghasilkan:
true / false
Maka keduanya sangat cocok:
movies.filter((movie) => {
return movie.name.includes(searchText);
});
Visual:
movie
↓
movie.name
↓
includes(...)
↓
true / false
↓
filter()
↙ ↘
true false
↓ ↓
simpan abaikan
42. Perhatikan searchText vs searchedMovies
Nama variable-nya memang mirip, tetapi berbeda.
searchText
Isi:
"enter"
Ini adalah input dari user.
searchedMovies
Isi:
[
movie1,
movie2
]
Ini adalah hasil pencarian.
Jadi:
searchText
→ apa yang dicari
searchedMovies
→ hasil yang ditemukan
43. Full flow project
Sekarang keseluruhan fitur:
┌─────────────────────────────┐
│ Search Box │
│ [ enter ] │
│ [Search] │
└─────────────────────────────┘
│
↓
user klik Search
│
↓
searchMovies()
│
↓
input.value
│
↓
"enter"
│
↓
movies.filter()
│
↓
cek setiap movie.name
│
↓
toLowerCase()
│
↓
includes()
│
┌────┴────┐
↓ ↓
true false
↓ ↓
simpan abaikan
│
↓
searchedMovies
│
↓
renderAllMovies()
│
↓
UI
44. Search vs Sort
Sekarang project kita sudah memiliki dua fitur yang berbeda.
Sort
movies.sort(...)
Tujuan:
mengubah URUTAN
Contoh:
A
B
C
↓
C
A
B
Search
movies.filter(...)
Tujuan:
memilih movie yang cocok
Contoh:
A
B
C
D
↓
B
D
Jadi:
sort → "urutkan"
filter → "pilih"
45. Search tidak mengubah urutan movies
Misalnya:
movies:
A
B
C
D
Search:
"C"
hasil:
searchedMovies:
C
Tapi:
movies:
A
B
C
D
tetap sama.
Itulah mengapa setelah search selesai, ketika kita menghapus search atau melakukan operasi lain, kita masih punya semua data asli.
46. Satu hal teknis kecil: trim()
Lecture ini belum membahasnya, tapi kamu sudah belajar trim(), jadi kamu bisa menghubungkannya.
Bayangkan user mengetik:
" enter "
Kalau langsung:
searchText.toLowerCase()
hasil:
" enter "
dan pencarian mungkin tidak sesuai harapan.
Kita bisa membersihkannya:
const searchText =
document
.getElementById("search-movie-text")
.value
.trim()
.toLowerCase();
Kemudian:
const searchedMovies = movies.filter((movie) => {
return movie.name
.toLowerCase()
.includes(searchText);
});
Ini versi yang lebih rapi.
Tapi untuk mengikuti lecture, cukup pahami:
toLowerCase()
includes()
filter()
dulu.
47. Pola penting yang mulai muncul di project
Perhatikan bagaimana setiap fitur punya function sendiri:
formatMovieName()
↓
format nama
formatMovieDescription()
↓
format description
formatGenre()
↓
format genre
displayStarRatings()
↓
format rating
displayMovieBanner()
↓
default image
sortMoviesByRating()
↓
sort rating
sortMoviesByReleaseYear()
↓
sort release year
searchMovies()
↓
search movie
Ini bagus karena setiap function mempunyai tanggung jawab yang jelas.
48. Kesalahan umum yang perlu dihindari
❌ 1. Lupa .value
Salah:
const searchText =
document.getElementById("search-movie-text");
Ini menghasilkan element object, bukan teks.
Benar:
const searchText =
document.getElementById("search-movie-text").value;
❌ 2. Lupa return di filter()
Salah:
movies.filter((movie) => {
movie.name.includes(searchText);
});
Callback tidak mengembalikan Boolean secara eksplisit.
Benar:
movies.filter((movie) => {
return movie.name.includes(searchText);
});
Atau arrow function concise:
movies.filter((movie) =>
movie.name.includes(searchText)
);
❌ 3. Tidak menggunakan toLowerCase()
movie.name.includes(searchText)
bisa gagal ketika casing berbeda.
Contoh:
Movie = Interstellar
Search = ENTER
→ false
Lebih baik:
movie.name.toLowerCase()
.includes(searchText.toLowerCase());
❌ 4. Menganggap filter() mengubah movies
Tidak.
const searchedMovies = movies.filter(...);
movies tetap.
Yang baru adalah:
searchedMovies
49. Cheat sheet lecture ini
| Konsep | Fungsi |
|---|---|
.value | mengambil teks dari input |
filter() | memilih movie yang memenuhi kondisi |
toLowerCase() | membuat huruf menjadi lowercase |
includes() | mengecek apakah String mengandung teks |
true | movie masuk hasil filter |
false | movie tidak masuk |
renderAllMovies() | menampilkan hasil ke UI |
innerHTML = "" | menghapus hasil render sebelumnya |
50. Mental model filter() yang harus kamu ingat
movies
↓
filter()
↓
cek satu per satu
↓
┌───────────────┐
│ kondisi benar?│
└───────────────┘
↓ ↓
YES NO
↓ ↓
simpan abaikan
↓
array baru
51. Mental model Search
Untuk fitur search:
SEARCH
│
↓
ambil input.value
│
↓
searchText
│
↓
movies.filter()
│
↓
movie.name
│
↓
toLowerCase()
│
↓
includes()
│
↓
true / false
│
↓
searchedMovies
│
↓
renderAllMovies()
│
↓
UI
52. Tiga method yang sekarang wajib bisa dibedakan
Ini menurut saya bagian paling penting setelah lecture ini.
┌────────────┬───────────────────────────────┐
│ forEach() │ lakukan sesuatu untuk semua │
├────────────┼───────────────────────────────┤
│ map() │ ubah setiap element │
├────────────┼───────────────────────────────┤
│ filter() │ pilih element yang cocok │
└────────────┴───────────────────────────────┘
Contoh project:
movies.forEach(...)
→ render setiap movie.
genres.map(...)
→ format setiap genre.
movies.filter(...)
→ cari movie yang cocok.
Dan ada:
movies.sort(...)
→ mengurutkan movie.
Jadi sekarang mental model array methods kamu mulai terbentuk:
forEach → KERJAKAN
map → UBAH
filter → PILIH
sort → URUTKAN
🧠 Kesimpulan lecture
Fitur Search ini sebenarnya bukan fitur yang rumit. Yang membuatnya terlihat panjang adalah karena beberapa konsep digabungkan.
Intinya hanya:
const searchText = input.value;
const searchedMovies = movies.filter((movie) => {
return movie.name
.toLowerCase()
.includes(searchText.toLowerCase());
});
renderAllMovies(searchedMovies);
Baca dalam bahasa manusia:
“Ambil teks yang diketik user, cari semua movie yang namanya mengandung teks tersebut tanpa memperhatikan huruf besar-kecil, kemudian tampilkan movie yang ditemukan.”
Dan ada satu mental model yang sangat bagus untuk kamu simpan:
sort()
→ mengubah URUTAN data
filter()
→ memilih SEBAGIAN data
renderAllMovies()
→ mengubah data menjadi TAMPILAN
Kalau tiga hal ini sudah benar-benar kamu pahami, fitur search, filter genre, sorting, dan kombinasi search + filter di project Synaphix berikutnya akan jauh lebih mudah dipahami.