MODUL 05 · PRAKTIK

Proyek Pertama: To-Do List

Saatnya menggabungkan HTML, CSS, dan JavaScript jadi satu — proyek nyata pertama yang benar-benar bisa kamu pakai sehari-hari.

Coba Dulu Hasil Akhirnya

Sebelum membahas kodenya, coba dulu aplikasi yang akan kamu bangun. Tambahkan beberapa tugas, centang yang sudah selesai, lalu hapus salah satunya.

Langkah 1: Struktur HTML

1Mulai dengan kerangka HTML: kolom input, tombol tambah, dan list kosong yang akan diisi oleh JavaScript.

<div class="todo-app">
  <div class="todo-input-row">
    <input type="text" id="todoInput" placeholder="Tulis tugas baru...">
    <button onclick="tambahTugas()">Tambah</button>
  </div>
  <ul class="todo-list" id="todoList"></ul>
</div>

Perhatikan: <ul id="todoList"> sengaja dikosongkan di HTML — isinya akan diisi secara dinamis oleh JavaScript setiap kali daftar tugas berubah.

Langkah 2: Styling dengan CSS

2Beri jarak dan tampilan dasar. Ini menggunakan konsep box model dan flexbox yang sudah dipelajari di Modul 03.

.todo-input-row {
  display: flex;
  gap: 8px;
  margin-bottom: 16px;
}

.todo-list li {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 4px;
  border-bottom: 1px solid #ddd;
}

.todo-list li.done span {
  text-decoration: line-through;
  color: gray;
}

Langkah 3: Logika JavaScript

3Simpan daftar tugas sebagai array of objects — setiap tugas punya teks dan status selesai/belum.

let daftarTugas = [];

function tambahTugas() {
  const input = document.getElementById("todoInput");
  const teks = input.value.trim();
  if (teks === "") return;          // jangan tambah tugas kosong
  daftarTugas.push({ teks: teks, selesai: false });
  input.value = "";
  renderTugas();
}

4Fungsi renderTugas() bertugas "menggambar ulang" seluruh list setiap kali ada perubahan — ini pola yang sangat umum dipakai di JavaScript.

function renderTugas() {
  const list = document.getElementById("todoList");
  list.innerHTML = ""; // kosongkan dulu sebelum digambar ulang

  daftarTugas.forEach((tugas, index) => {
    const li = document.createElement("li");
    if (tugas.selesai) li.classList.add("done");
    li.innerHTML = `
      <input type="checkbox" onchange="toggleTugas(${index})">
      <span>${tugas.teks}</span>
      <button onclick="hapusTugas(${index})">Hapus</button>
    `;
    list.appendChild(li);
  });
}

5Terakhir, fungsi untuk mengubah status selesai dan menghapus tugas:

function toggleTugas(index) {
  daftarTugas[index].selesai = !daftarTugas[index].selesai;
  renderTugas();
}

function hapusTugas(index) {
  daftarTugas.splice(index, 1); // hapus 1 item di posisi `index`
  renderTugas();
}

Langkah 4: Menyimpan Data dengan Local Storage

Masalah dengan kode di atas: kalau halaman di-refresh, semua tugas hilang karena hanya tersimpan di memori sementara (variabel JavaScript). Local storage adalah cara browser menyimpan data secara permanen di perangkat pengguna, sampai dihapus secara manual.

// Menyimpan data ke local storage (ubah jadi teks dulu dengan JSON.stringify)
function simpanData() {
  localStorage.setItem("tugas", JSON.stringify(daftarTugas));
}

// Mengambil data saat halaman dimuat
function muatData() {
  const data = localStorage.getItem("tugas");
  if (data) {
    daftarTugas = JSON.parse(data);
  }
}

// Panggil simpanData() di akhir setiap fungsi tambah/toggle/hapus
// Panggil muatData() sekali saat halaman pertama kali dibuka
Kenapa Perlu JSON.stringify?

Local storage hanya bisa menyimpan teks (string), bukan array atau object langsung. JSON.stringify() mengubah data JavaScript jadi teks, dan JSON.parse() mengubahnya kembali jadi data JavaScript saat diambil.

Langkah 5: Debugging — Saat Kode Tidak Berjalan

Hampir pasti kode pertamamu tidak akan langsung berjalan sempurna. Ini normal — bahkan programmer berpengalaman mengalaminya setiap hari. Beberapa kebiasaan debugging dasar:

Error Paling Umum untuk Pemula

Uncaught ReferenceError: x is not defined — biasanya berarti kamu memanggil variabel/fungsi yang belum dideklarasikan, atau ada typo pada namanya.

Latihan Pengembangan

Setelah memahami kode di atas, coba kembangkan sendiri kemampuan to-do list ini:

  1. Tambahkan fitur "Hapus Semua Tugas yang Selesai"
  2. Tambahkan penghitung jumlah tugas yang belum selesai
  3. Tambahkan local storage agar data tidak hilang saat refresh (lihat Langkah 4)

Latihan Pemahaman

Soal 1: Mengapa fungsi renderTugas() perlu mengosongkan list (innerHTML = "") di awal?
Jawaban: Agar list tidak menumpuk item lama setiap kali fungsi dipanggil ulang — list digambar ulang dari nol setiap ada perubahan data.
Soal 2: Apa fungsi dari JSON.stringify() saat menyimpan data ke local storage?
Jawaban: Mengubah array atau object JavaScript menjadi teks (string), karena local storage hanya bisa menyimpan data dalam bentuk teks.

Rangkuman Modul Ini

Selamat — kamu baru menyelesaikan proyek pertamamu! Modul terakhir akan membahas ke mana arah belajarmu selanjutnya setelah fondasi ini selesai.