Spaces:
Running
Running
| const todoList = document.getElementById('todo-list'); | |
| const taskInput = document.getElementById('task-input'); | |
| const todoForm = document.getElementById('todo-form'); | |
| let tasks = []; | |
| // --- Local Storage Management --- | |
| function loadTasks() { | |
| // Attempt to retrieve tasks from localStorage | |
| const storedTasks = localStorage.getItem('todoTasks'); | |
| if (storedTasks) { | |
| try { | |
| tasks = JSON.parse(storedTasks); | |
| } catch (e) { | |
| console.error("Error parsing tasks from localStorage:", e); | |
| tasks = []; | |
| } | |
| } | |
| } | |
| function saveTasks() { | |
| localStorage.setItem('todoTasks', JSON.stringify(tasks)); | |
| } | |
| // --- Rendering --- | |
| function renderTasks() { | |
| // Clear the current list content | |
| todoList.innerHTML = ''; | |
| if (tasks.length === 0) { | |
| todoList.innerHTML = '<li class="empty-state">No tasks yet! Add one above.</li>'; | |
| return; | |
| } | |
| // Use a DocumentFragment for performance when appending many nodes | |
| const fragment = document.createDocumentFragment(); | |
| tasks.forEach((task, index) => { | |
| const listItem = document.createElement('li'); | |
| listItem.classList.add('todo-item'); | |
| listItem.setAttribute('aria-label', `Task: ${task.text}, ${task.completed ? 'Completed' : 'Pending'}`); | |
| if (task.completed) { | |
| listItem.classList.add('completed'); | |
| } | |
| const taskContent = document.createElement('span'); | |
| taskContent.classList.add('task-content'); | |
| taskContent.textContent = task.text; | |
| taskContent.setAttribute('role', 'button'); | |
| taskContent.setAttribute('tabindex', '0'); // Make span focusable | |
| // Event listener for toggling completion (Mark as Complete) | |
| taskContent.addEventListener('click', () => toggleComplete(index)); | |
| taskContent.addEventListener('keydown', (e) => { | |
| if (e.key === 'Enter' || e.key === ' ') { | |
| e.preventDefault(); | |
| toggleComplete(index); | |
| } | |
| }); | |
| const deleteButton = document.createElement('button'); | |
| deleteButton.classList.add('delete-btn'); | |
| deleteButton.innerHTML = '✕'; // X mark | |
| deleteButton.setAttribute('aria-label', `Delete task: ${task.text}`); | |
| // Event listener for deletion | |
| deleteButton.addEventListener('click', () => deleteTask(index)); | |
| listItem.appendChild(taskContent); | |
| listItem.appendChild(deleteButton); | |
| fragment.appendChild(listItem); | |
| }); | |
| todoList.appendChild(fragment); | |
| } | |
| // --- Core Functionality --- | |
| function addTask(e) { | |
| e.preventDefault(); | |
| const text = taskInput.value.trim(); | |
| if (text) { | |
| const newTask = { | |
| id: Date.now(), | |
| text: text, | |
| completed: false | |
| }; | |
| // Add new task to the beginning of the array | |
| tasks.unshift(newTask); | |
| taskInput.value = ''; | |
| saveTasks(); | |
| renderTasks(); | |
| // Announce the addition for screen readers (optional but good practice) | |
| // Note: Using aria-live="polite" on the UL generally handles this, | |
| // but focusing the input might be enough feedback. | |
| taskInput.focus(); | |
| } | |
| } | |
| function toggleComplete(index) { | |
| if (index >= 0 && index < tasks.length) { | |
| tasks[index].completed = !tasks[index].completed; | |
| saveTasks(); | |
| renderTasks(); | |
| } | |
| } | |
| function deleteTask(index) { | |
| if (index >= 0 && index < tasks.length) { | |
| const taskText = tasks[index].text; | |
| // Simple confirmation before deletion | |
| if (confirm(`Are you sure you want to delete "${taskText}"?`)) { | |
| tasks.splice(index, 1); | |
| saveTasks(); | |
| renderTasks(); | |
| } | |
| } | |
| } | |
| // --- Initialization --- | |
| document.addEventListener('DOMContentLoaded', () => { | |
| loadTasks(); | |
| renderTasks(); | |
| // Setup event listener for the form submission | |
| todoForm.addEventListener('submit', addTask); | |
| }); |