JavaScript  

Basic To-Do List App Javascript

The task idea you've proposed is excellent! A To-Do List app is a perfect project for practicing core web development skills with JavaScript. You've already outlined great improvements to the basic functionality, and they will definitely add value to the app. Let’s break it down into an actionable plan, and I'll guide you step-by-step through the features and improvements, including code examples where needed.

Initial Steps: Basic To-Do List App

1. HTML Structure

Start by creating the basic HTML layout for the app.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>To-Do List</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div class="todo-container">
        <h1>My To-Do List</h1>
        <input type="text" id="taskInput" placeholder="Add a new task">
        <button id="addTaskButton">Add</button>
        <ul id="taskList"></ul>
    </div>
    
    <script src="app.js"></script>
</body>
</html>

2. CSS Styling

Add simple CSS to make the app look clean and user-friendly.

body {
    font-family: Arial, sans-serif;
    background-color: #f4f4f9;
    margin: 0;
    padding: 0;
}

.todo-container {
    width: 300px;
    margin: 50px auto;
    padding: 20px;
    background-color: white;
    border-radius: 8px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

h1 {
    text-align: center;
    color: #333;
}

input {
    width: 70%;
    padding: 10px;
    margin-right: 10px;
    border: 1px solid #ccc;
    border-radius: 4px;
}

button {
    padding: 10px;
    background-color: #4CAF50;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}

button:hover {
    background-color: #45a049;
}

ul {
    list-style-type: none;
    padding: 0;
}

li {
    display: flex;
    justify-content: space-between;
    padding: 10px;
    border-bottom: 1px solid #ddd;
}

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

input[type="date"] {
    margin-left: 10px;
    padding: 5px;
    width: 90px;
}

3. JavaScript Functionality

Implement the core logic for adding, deleting, and marking tasks as complete, and saving the tasks in localStorage.

Basic Features (Add, Delete, Mark Completed, and Persist Tasks):

// Select DOM elements
const taskInput = document.getElementById('taskInput');
const addTaskButton = document.getElementById('addTaskButton');
const taskList = document.getElementById('taskList');

// Load tasks from localStorage
let tasks = JSON.parse(localStorage.getItem('tasks')) || [];

// Function to render tasks
function renderTasks() {
    taskList.innerHTML = '';  // Clear the list
    tasks.forEach((task, index) => {
        const li = document.createElement('li');
        li.classList.toggle('completed', task.completed);

        const taskText = document.createElement('span');
        taskText.textContent = task.text;
        taskText.addEventListener('click', () => toggleTaskCompletion(index));

        // Edit task button
        const editButton = document.createElement('button');
        editButton.textContent = 'Edit';
        editButton.addEventListener('click', () => editTask(index));

        // Delete task button
        const deleteButton = document.createElement('button');
        deleteButton.textContent = 'Delete';
        deleteButton.addEventListener('click', () => deleteTask(index));

        // Add the due date
        const dueDateText = document.createElement('span');
        if (task.dueDate) {
            dueDateText.textContent = ` (Due: ${task.dueDate})`;
        }

        li.appendChild(taskText);
        li.appendChild(dueDateText);
        li.appendChild(editButton);
        li.appendChild(deleteButton);
        taskList.appendChild(li);
    });
}

// Add a task
function addTask() {
    const taskText = taskInput.value.trim();
    if (taskText === '') return;

    const dueDate = document.getElementById('dueDateInput') ? document.getElementById('dueDateInput').value : '';
    tasks.push({ text: taskText, completed: false, dueDate });
    taskInput.value = ''; // Clear input field
    saveTasks();
    renderTasks();
}

// Toggle task completion
function toggleTaskCompletion(index) {
    tasks[index].completed = !tasks[index].completed;
    saveTasks();
    renderTasks();
}

// Delete task
function deleteTask(index) {
    tasks.splice(index, 1);  // Remove task from array
    saveTasks();
    renderTasks();
}

// Edit task
function editTask(index) {
    taskInput.value = tasks[index].text; // Set the input field with the task text
    addTaskButton.textContent = 'Save'; // Change button text to "Save"

    // When "Save" is clicked, update the task
    addTaskButton.onclick = function() {
        tasks[index].text = taskInput.value.trim(); // Update task text
        taskInput.value = '';
        addTaskButton.textContent = 'Add';  // Reset button text
        saveTasks();
        renderTasks();
    };
}

// Save tasks to localStorage
function saveTasks() {
    localStorage.setItem('tasks', JSON.stringify(tasks));
}

// Event listener to add tasks
addTaskButton.addEventListener('click', addTask);

// Initial render
renderTasks();

4. Improvements

Edit Tasks

The editTask() function allows the user to click on the Edit button next to each task to modify it.

  • When editing, the task's text appears in the input field.

  • After clicking "Save", the task is updated in the tasks array, and the list is re-rendered.

Sort Tasks

Add sorting functionality to allow users to sort tasks by completion status or by due date.

Example: Sort by Completion Status

function sortTasksByCompletion() {
    tasks.sort((a, b) => a.completed - b.completed);  // Move completed tasks to the bottom
    saveTasks();
    renderTasks();
}

const sortButton = document.createElement('button');
sortButton.textContent = 'Sort by Completion';
document.body.insertBefore(sortButton, taskList);

sortButton.addEventListener('click', sortTasksByCompletion);

You can extend this to sort by due date or other criteria.

Responsive Design

To make the app responsive, you can use CSS media queries to adjust the layout for mobile and tablet screens.

Example Media Query

@media (max-width: 600px) {
    .todo-container {
        width: 100%;
        margin: 20px;
    }

    input {
        width: 70%;
    }

    button {
        width: 25%;
    }

    li {
        font-size: 14px;
    }
}

Due Dates

You can allow users to set a due date for tasks and display it alongside the task.

Example Code

<!-- Add a date input field in the HTML -->
<input type="date" id="dueDateInput" />

In the addTask() function, we store the due date with each task:

const dueDate = document.getElementById('dueDateInput').value;
tasks.push({ text: taskText, completed: false, dueDate: dueDate || '' });

Display the due date when rendering tasks:

const dueDateText = document.createElement('span');
if (task.dueDate) {
    dueDateText.textContent = ` (Due: ${task.dueDate})`;
}

Animations

Adding simple animations can make the app more dynamic. Use CSS transitions or animations for task addition/removal.

Example: Fade-in Animation for New Tasks:

@keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}

li {
    animation: fadeIn 0.5s ease-in-out;
}

You can also animate the removal of tasks:

function deleteTask(index) {
    const li = taskList.children[index];
    li.style.animation = 'fadeOut 0.5s forwards'; // Apply fade-out animation
    setTimeout(() => {
        tasks.splice(index, 1); // Remove task
        saveTasks();
        renderTasks();
    }, 500);  // Wait for animation to complete
}

CSS for Fade-out

@keyframes fadeOut {
    from { opacity: 1; }
    to { opacity: 0; }
}