Proyek Mini: Todo List
Proyek capstone DOM: bangun aplikasi todo lengkap dari nol. Menggabungkan seleksi elemen, event, delegation, classList, dan localStorage dalam satu aplikasi nyata dengan arsitektur render-ulang dari single source of truth.
Proyek Capstone: Kenapa Todo List?
Todo list terlihat sederhana, tapi ia memaksa memakai SEMUA yang kamu pelajari di modul DOM: seleksi elemen, membuat elemen, event listener, delegation, classList, traversal, dan localStorage. Aplikasi produksi, dari kasir sampai dashboard admin, pada dasarnya adalah pola yang sama: data di memori, tampilkan ke layar, tangkap aksi user, simpan perubahan. Kuasai pola ini sekali, dan kamu bisa membangun aplikasi CRUD apa pun.
Arsitektur: Satu Sumber Kebenaran
Aturan emas proyek ini: array todos di memori adalah SATU-SATUNYA sumber kebenaran. DOM hanyalah cermin: setiap ada perubahan, render ulang list dari array. Jangan pernah membaca state dari DOM (mis. menghitung li yang punya class selesai), karena DOM dan data bisa tidak sinkron dan bug-nya sulit dilacak.
// Struktur data: tiap todo punya id unik, teks, dan status
let todos = baca("todos", []);
function simpanSemua() {
simpan("todos", todos);
}
function render() {
const ul = document.getElementById("daftar");
ul.innerHTML = ""; // kosongkan, lalu bangun ulang dari data
todos.forEach(todo => {
const li = document.createElement("li");
li.dataset.id = todo.id;
li.classList.toggle("selesai", todo.selesai);
const label = document.createElement("span");
label.textContent = todo.teks;
const btnHapus = document.createElement("button");
btnHapus.textContent = "Hapus";
btnHapus.className = "hapus";
li.append(label, btnHapus);
ul.appendChild(li);
});
const sisa = todos.filter(t => !t.selesai).length;
document.getElementById("info").textContent =
sisa === 0 ? "Semua selesai!" : sisa + " tersisa";
}dataset.id menyimpan id di atribut data-id, cara standar menautkan elemen DOM ke data tanpa variabel global berantakan.
Aksi: Tambah, Toggle, Hapus
Tiga fungsi murni mengubah data, lalu simpan dan render ulang. Perhatikan polanya selalu sama: ubah array, simpan, render.
function tambah(teks) {
const bersih = teks.trim();
if (!bersih) return;
todos.push({ id: Date.now(), teks: bersih, selesai: false });
simpanSemua();
render();
}
function toggle(id) {
const todo = todos.find(t => t.id === id);
if (todo) {
todo.selesai = !todo.selesai;
simpanSemua();
render();
}
}
function hapus(id) {
todos = todos.filter(t => t.id !== id);
simpanSemua();
render();
}Event: Delegation untuk List Dinamis
Item dibuat setelah halaman dimuat, jadi delegation wajib (modul event delegation). Satu listener di <ul> menangani klik label (toggle) dan tombol hapus:
document.getElementById("daftar").addEventListener("click", (e) => {
const li = e.target.closest("li");
if (!li) return;
const id = Number(li.dataset.id);
if (e.target.closest(".hapus")) {
hapus(id);
} else {
toggle(id);
}
});
document.getElementById("form").addEventListener("submit", (e) => {
e.preventDefault();
const input = document.getElementById("input-todo");
tambah(input.value);
input.value = "";
input.focus(); // UX bagus: siap ketik tugas berikutnya
});Fungsi baca dan simpan memakai pola aman dari modul localStorage (try-catch + fallback + JSON).
Fitur Wajib dan Kriteria
- Input + tombol "Tambah": tugas baru muncul di list, input dikosongkan dan difokuskan lagi.
- Klik tugas: toggle selesai (class
selesai+ coret via CSStext-decoration: line-through). - Tombol hapus per tugas, memakai event delegation.
- Data tersimpan di localStorage: refresh halaman tidak menghilangkan tugas.
- Penghitung sisa: "3 tersisa" atau "Semua selesai!".
Tulis HTML+JS dari nol di editor tugas di bawah, lalu minta penilaian AI. Tidak ada playground jadi: kerjakan mandiri, ini simulasi kerja nyata.
Jebakan Umum Proyek Ini
- Membaca state dari DOM alih-alih dari array
todos. Begitu render ulang, semua info hilang. - Id duplikat: pakai
Date.now()atau counter naik, jangan pakai index array (berubah saat item dihapus). - Lupa memanggil
simpanSemua()di salah satu aksi: data hilang saat refresh dan bug-nya hanya muncul belakangan. - Memasang listener per-li hasil render: setelah render ulang, listener lama ikut terhapus bersama elemennya. Delegation menyelesaikan ini permanen.
Ringkasan
Todo list mengajarkan arsitektur aplikasi front-end sejati: single source of truth di memori, DOM sebagai cermin yang di-render ulang, aksi sebagai fungsi kecil (tambah/toggle/hapus), event delegation untuk konten dinamis, dan localStorage untuk persistensi. Pola persis ini dipakai aplikasi CRUD di dunia kerja, hanya datanya yang diganti.
Tantangan
Pemanasan: Render List
Pemanasan: diberi array todos. Tulis fungsi render() yang mengosongkan #daftar lalu mengisi li per todo (pakai textContent). Fungsi render-ulang-dari-data adalah inti arsitektur proyek ini dan pola standar aplikasi CRUD profesional: setiap perubahan data selalu diikuti render ulang agar tampilan tidak pernah basi.
<!doctype html> <html> <head> <meta charset="utf-8" /> </head> <body> <h1>Halo JS</h1> <p>Buka console preview untuk melihat output.</p> <script src="index.js"></script> </body> </html>
Tugas
Aplikasi Todo List
Bangun aplikasi todo lengkap: tambah tugas, toggle selesai, hapus per item (delegation), simpan ke localStorage, tampilkan sisa tugas. Tulis HTML+JS dari nol di editor bawah.
Kriteria penilaian:
- Semua 5 fitur wajib berfungsi
- Menggunakan event delegation untuk hapus/toggle
- Data bertahan setelah refresh (localStorage + JSON)
- Kode terstruktur (fungsi render/tambah/hapus terpisah)
AI tutor akan memeriksa bug, kesalahan syntax, dan memberi saran perbaikan.