Membuat Aplikasi To Do List Sederhana dengan JavaScript: Project Fundamental untuk Menguasai DOM

Membuat Aplikasi To Do List Sederhana dengan JavaScript: Project Fundamental untuk Menguasai DOM

Membuat Aplikasi To Do List Sederhana dengan JavaScript - Ilustrasi AI

Setiap pengembang frontend pasti pernah membuat atau setidaknya mencoba membuat aplikasi To Do List. Mengapa? Karena aplikasi sederhana ini adalah "Hello World" sejati dalam konteks manipulasi Document Object Model (DOM) dan interaksi pengguna. Untuk siapa pun yang baru memulai atau ingin memperkuat dasar-dasar pengembangan web, menyelesaikan project javascript ini adalah langkah fundamental yang sangat penting.

Artikel ini akan memandu Anda secara mendalam, langkah demi langkah, dari persiapan struktur HTML dasar hingga implementasi JavaScript yang kompleks, termasuk penanganan event, manipulasi DOM, dan penyimpanan data menggunakan Local Storage. Setelah mengikuti panduan ini, Anda tidak hanya akan memiliki aplikasi To Do List yang berfungsi, tetapi juga pemahaman yang kuat tentang bagaimana JavaScript bekerja di sisi klien.

Mengapa Memilih To Do List sebagai Project JavaScript Pertama Anda?

To Do List, meskipun terlihat sederhana, memaksa pengembang untuk berinteraksi dengan tiga pilar utama pengembangan web modern: HTML (Struktur), CSS (Penampilan), dan JavaScript (Logika). Secara spesifik, project ini mengajarkan beberapa keterampilan inti:

  • Manipulasi DOM: Anda belajar cara membuat, memodifikasi, dan menghapus elemen HTML secara dinamis, yang merupakan jantung dari hampir semua aplikasi web interaktif.
  • Penanganan Event (Event Handling): Anda akan mengimplementasikan fungsi yang merespons input pengguna, seperti klik tombol, penekanan tombol Enter, dan pengiriman formulir.
  • Manajemen State Sederhana: Anda belajar melacak status tugas (misalnya, apakah sudah selesai atau belum) dan memastikan status tersebut dipertahankan, terutama melalui Local Storage.
  • Struktur Kode Bersih: Meskipun project ini kecil, ia mendorong praktik pemisahan tugas (separation of concerns), membuat kode Anda lebih terorganisir.

Fokus utama project javascript ini adalah penggunaan JavaScript murni (Vanilla JS), menghindari penggunaan framework atau library untuk memastikan Anda benar-benar memahami mekanika di baliknya.

Persiapan Awal: Struktur HTML dan CSS Dasar

Sebelum kita terjun ke JavaScript, kita perlu menyiapkan wadah untuk aplikasi kita. Struktur HTML akan terdiri dari satu input field, satu tombol untuk menambahkan tugas, dan satu daftar kosong (<ul>) tempat tugas-tugas akan muncul. Struktur ini disebut sebagai boilerplate.

Boilerplate HTML (index.html)

Simpan kode berikut dalam file index.html. Perhatikan bahwa kita memberikan ID spesifik pada elemen-elemen yang akan diinteraksi oleh JavaScript.


<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>To Do List Project JavaScript</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="container">
        <h1>Daftar Tugas Harian</h1>
        
        <div class="input-area">
            <input type="text" id="taskInput" placeholder="Tambahkan tugas baru...">
            <button id="addTaskBtn">Tambah</button>
        </div>

        <ul id="taskList">
            <!-- Tugas akan ditambahkan di sini oleh JavaScript -->
        </ul>
    </div>

    <script src="script.js"></script>
</body>
</html>
    

Styling Minimalis (style.css)

Untuk membuat tampilan lebih menarik dan fungsional, kita akan menerapkan CSS dasar. Styling ini memastikan kontainer berada di tengah, dan elemen daftar memiliki tampilan yang bersih.


body {
    font-family: Arial, sans-serif;
    background-color: #f4f4f9;
    display: flex;
    justify-content: center;
    padding-top: 50px;
}

.container {
    background: white;
    padding: 30px;
    border-radius: 10px;
    box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
    width: 100%;
    max-width: 450px;
}

.input-area {
    display: flex;
    margin-bottom: 20px;
}

#taskInput {
    flex-grow: 1;
    padding: 10px;
    border: 1px solid #ccc;
    border-radius: 5px 0 0 5px;
    font-size: 16px;
}

#addTaskBtn {
    padding: 10px 15px;
    background-color: #007bff;
    color: white;
    border: none;
    border-radius: 0 5px 5px 0;
    cursor: pointer;
}

#taskList {
    list-style: none;
    padding: 0;
}

#taskList li {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 10px;
    margin-bottom: 8px;
    background: #e9ecef;
    border-radius: 5px;
    transition: all 0.3s;
}

#taskList li.completed {
    text-decoration: line-through;
    color: #888;
    background: #d4d8db;
}

.delete-btn {
    background: #dc3545;
    color: white;
    border: none;
    padding: 5px 10px;
    border-radius: 4px;
    cursor: pointer;
}
    

Inti Logika JavaScript: Manipulasi DOM

Sekarang saatnya menghidupkan aplikasi kita. Semua logika akan berada dalam file script.js.

Menangkap Elemen Kunci (Selectors)

Langkah pertama dalam setiap project javascript yang melibatkan DOM adalah mendapatkan referensi ke elemen-elemen yang akan dimanipulasi.


// script.js

// 1. Dapatkan referensi elemen-elemen penting
const taskInput = document.getElementById('taskInput');
const addTaskBtn = document.getElementById('addTaskBtn');
const taskList = document.getElementById('taskList');
    

Fungsi Utama 1: Menambahkan Tugas Baru (addTask())

Fungsi ini bertanggung jawab untuk mengambil nilai dari input, memvalidasinya, dan kemudian membuat elemen HTML baru (<li>) untuk ditambahkan ke daftar tugas.

Kami akan menggunakan metode document.createElement() dan appendChild(). Ini adalah teknik dasar manipulasi DOM.


function addTask() {
    const taskText = taskInput.value.trim();

    if (taskText === "") {
        alert("Tugas tidak boleh kosong!");
        return; // Hentikan eksekusi jika input kosong
    }

    // A. Buat elemen LI baru
    const listItem = document.createElement('li');
    listItem.innerHTML = `
        <span class="task-text">${taskText}</span>
        <button class="delete-btn">Hapus</button>
    `;

    // B. Tambahkan event listener untuk menandai selesai (klik pada teks tugas)
    listItem.querySelector('.task-text').addEventListener('click', function() {
        listItem.classList.toggle('completed');
        saveTasks(); // Simpan perubahan status
    });
    
    // C. Tambahkan event listener untuk menghapus tugas
    listItem.querySelector('.delete-btn').addEventListener('click', function() {
        listItem.remove(); // Hapus elemen LI dari DOM
        saveTasks(); // Simpan perubahan
    });

    // D. Masukkan LI ke dalam UL (taskList)
    taskList.appendChild(listItem);

    // E. Bersihkan input field setelah tugas ditambahkan
    taskInput.value = '';

    // F. Simpan ke Local Storage
    saveTasks(); 
}

// 3. Tambahkan event listener ke tombol 'Tambah'
addTaskBtn.addEventListener('click', addTask);

// Opsional: Tambahkan fungsi untuk menambahkan tugas saat menekan 'Enter'
taskInput.addEventListener('keypress', function(e) {
    if (e.key === 'Enter') {
        addTask();
    }
});
    

Fungsi Utama 2: Delegasi Event untuk Efisiensi (Penjelasan Mendalam)

Pada kode di atas, kita menambahkan dua event listener untuk setiap tugas baru. Ini bisa menjadi kurang efisien jika kita memiliki ribuan tugas. Meskipun untuk To Do List sederhana cara di atas sudah memadai, sebagai pakar SEO dan pemrograman, penting untuk memperkenalkan konsep yang lebih baik: Event Delegation.

Event Delegation adalah teknik di mana kita hanya menempatkan satu event listener pada elemen induk (#taskList), dan kemudian menggunakan properti event.target untuk menentukan elemen anak mana yang sebenarnya diklik.

Mari kita refaktor bagian event listener menggunakan Event Delegation (Catatan: Ini adalah alternatif yang lebih canggih dari kode di atas):


// Menggunakan Event Delegation pada elemen induk: taskList
taskList.addEventListener('click', function(e) {
    const target = e.target;

    // A. Jika yang diklik adalah tombol "Hapus"
    if (target.classList.contains('delete-btn')) {
        // target.parentElement adalah LI yang menaunginya
        target.parentElement.remove();
        saveTasks();
    }
    
    // B. Jika yang diklik adalah teks tugas (untuk menandai selesai/belum)
    if (target.classList.contains('task-text')) {
        // target.parentElement adalah LI
        target.parentElement.classList.toggle('completed');
        saveTasks();
    }
});

// Catatan: Jika menggunakan Event Delegation, Anda tidak perlu menambahkan listener 
// secara individual di dalam fungsi addTask(). Anda hanya perlu memastikan 
// elemen yang dibuat memiliki class yang tepat (task-text, delete-btn).
    

Penyempurnaan: Menyimpan Data dengan Local Storage

Saat ini, aplikasi kita berfungsi, tetapi jika pengguna me-refresh halaman, semua data akan hilang. Untuk membuat aplikasi ini benar-benar fungsional sebagai project javascript yang lengkap, kita harus menggunakan Web Storage API, khususnya Local Storage, untuk menyimpan data secara persisten di browser pengguna.

Mengapa Local Storage Penting?

Local Storage memungkinkan kita menyimpan pasangan kunci-nilai (key-value pairs) yang bertahan bahkan setelah sesi browser ditutup. Data disimpan sebagai string, jadi kita harus menggunakan JSON.stringify() saat menyimpan array/objek dan JSON.parse() saat mengambilnya kembali.

Implementasi Save dan Load

Kita memerlukan dua fungsi: saveTasks() untuk menyimpan status saat ini, dan loadTasks() untuk memuat data saat aplikasi pertama kali dibuka.


// 4. Fungsi untuk Menyimpan Tugas ke Local Storage
function saveTasks() {
    // Ambil semua elemen LI di daftar tugas
    const tasks = [];
    taskList.querySelectorAll('li').forEach(listItem => {
        tasks.push({
            text: listItem.querySelector('.task-text').innerText,
            completed: listItem.classList.contains('completed')
        });
    });

    // Simpan array objek ke Local Storage sebagai string JSON
    localStorage.setItem('todoListTasks', JSON.stringify(tasks));
}

// 5. Fungsi untuk Memuat Tugas dari Local Storage
function loadTasks() {
    const storedTasks = localStorage.getItem('todoListTasks');
    if (storedTasks) {
        const tasks = JSON.parse(storedTasks);

        tasks.forEach(task => {
            // Buat ulang elemen LI berdasarkan data yang tersimpan
            const listItem = document.createElement('li');
            listItem.innerHTML = `
                <span class="task-text">${task.text}</span>
                <button class="delete-btn">Hapus</button>
            `;

            // Terapkan status 'completed' jika ada
            if (task.completed) {
                listItem.classList.add('completed');
            }

            // Pastikan listener untuk aksi (completed/delete) tetap berfungsi
            // Jika Anda menggunakan Event Delegation, Anda TIDAK perlu menambahkan listener di sini.
            // Jika Anda menggunakan metode lama (individual listener), Anda harus menambahkannya di sini.
            
            // Karena kita menggunakan Event Delegation di kode refaktor sebelumnya, 
            // kita hanya perlu menambahkan listItem ke taskList
            taskList.appendChild(listItem);
        });
    }
}

// 6. Panggil loadTasks() saat halaman pertama kali dimuat
document.addEventListener('DOMContentLoaded', loadTasks);
    

Dengan integrasi Local Storage, project javascript Anda sekarang memiliki memori. Setiap kali pengguna menambahkan, menandai selesai, atau menghapus tugas, fungsi saveTasks() dipanggil, dan data diperbarui secara persisten.

Kesalahan Umum yang Sering Terjadi dalam Project To Do List

Meskipun To Do List terlihat sederhana, ada beberapa jebakan umum yang sering dihadapi oleh pengembang pemula:

  1. Kesalahan Penggunaan .innerHTML pada Input: Pengembang sering mencoba mendapatkan nilai input menggunakan taskInput.innerHTML. Ingat, untuk elemen formulir seperti <input>, Anda harus selalu menggunakan properti .value.
  2. Lupa Memanggil saveTasks(): Fungsi penyimpanan harus dipanggil di setiap titik di mana status daftar berubah (menambah, menghapus, atau menandai selesai). Jika Anda lupa memanggilnya, perubahan tidak akan disimpan.
  3. Masalah JSON Parsing: Saat mengambil data dari Local Storage, jika kuncinya tidak ada (misalnya, ini adalah kunjungan pertama pengguna), localStorage.getItem('kunci') akan mengembalikan null. Jika Anda mencoba menjalankan JSON.parse(null), ini bisa menyebabkan error di beberapa lingkungan lama. Selalu lakukan pemeriksaan if (storedTasks) sebelum parsing.
  4. Kehilangan Event Listener Setelah Load: Jika Anda tidak menggunakan Event Delegation, Anda harus memastikan bahwa saat loadTasks() membuat ulang elemen LI, listener individual (untuk hapus dan selesai) juga ikut ditambahkan kembali.

FAQ (Pertanyaan Sering Diajukan)

Q: Bisakah saya menggunakan Local Storage untuk menyimpan data sensitif?

A: Tidak disarankan. Local Storage tidak terenkripsi dan dapat diakses melalui JavaScript. Ini ideal untuk menyimpan preferensi pengguna non-sensitif atau data sesi. Untuk data sensitif, gunakan solusi sisi server yang aman.

Q: Apa perbedaan antara .innerText dan .innerHTML?

A: .innerText hanya mengembalikan atau mengatur konten teks murni dari suatu elemen. Sementara itu, .innerHTML mengembalikan atau mengatur konten, termasuk semua markup HTML di dalamnya. Dalam project javascript ini, kami menggunakan .innerHTML saat membuat elemen <li> karena kami menyertakan tag <span> dan <button>.

Q: Bagaimana cara mengatur prioritas tugas dalam aplikasi ini?

A: Anda bisa memodifikasi fungsi addTask() untuk juga menerima input prioritas (misalnya, drop-down High/Medium/Low). Kemudian, saat memuat daftar, Anda bisa menggunakan CSS untuk memberikan warna latar belakang yang berbeda (misalnya, merah untuk High) atau mengimplementasikan fungsi penyortiran (sort) pada array tugas sebelum menampilkannya.

Q: Mengapa saya harus menggunakan Event Delegation?

A: Event Delegation mengurangi beban memori karena Anda hanya memiliki satu event listener (pada elemen induk) alih-alih ratusan listener (pada setiap elemen anak). Ini sangat meningkatkan kinerja, terutama pada daftar yang sangat panjang, dan juga memudahkan penanganan elemen yang ditambahkan ke DOM setelah pemuatan awal.

Kesimpulan dan Langkah Selanjutnya

Anda telah berhasil menyelesaikan project javascript dasar namun vital: aplikasi To Do List yang berfungsi penuh, lengkap dengan kemampuan CRUD (Create, Read, Update, Delete) dan penyimpanan data persisten melalui Local Storage. Pemahaman tentang manipulasi DOM, penanganan event, dan manajemen data sederhana adalah landasan yang akan Anda gunakan dalam setiap framework frontend modern, baik itu React, Vue, atau Angular.

Untuk meningkatkan keterampilan Anda, coba kembangkan project ini lebih lanjut dengan menambahkan fitur-fitur berikut:

  1. Fungsi Edit: Tambahkan tombol 'Edit' yang mengubah teks tugas menjadi input field, memungkinkan pengguna untuk memodifikasi tugas yang sudah ada.
  2. Filter: Tambahkan tombol filter untuk menampilkan hanya tugas yang 'Selesai' atau 'Belum Selesai'.
  3. Animasi CSS: Terapkan transisi atau animasi kecil ketika tugas ditambahkan atau dihapus untuk meningkatkan pengalaman pengguna.
  4. Validasi Input Lanjutan: Implementasikan validasi yang lebih kuat, seperti membatasi panjang karakter atau mencegah karakter berbahaya.

Teruslah berlatih dengan proyek-proyek kecil seperti ini. Konsistensi adalah kunci untuk menguasai pengembangan web.



Posting Komentar

Lebih baru Lebih lama