Advanced To-Do App: Categories, Filters, and Search
This version allows you to:
- Assign categories to tasks
- Filter tasks by category or completion status
- Search tasks by text
HTML Structure
<div id="todo-app">
<h1>Advanced To-Do List</h1>
<input type="text" id="task-input" placeholder="Add a task" />
<select id="category-select">
<option value="General">General</option>
<option value="Work">Work</option>
<option value="Personal">Personal</option>
</select>
<button id="add-btn">Add Task</button>
<div id="filters">
<label>Show:
<select id="filter-status">
<option value="all">All</option>
<option value="completed">Completed</option>
<option value="pending">Pending</option>
</select>
</label>
<label>Category:
<select id="filter-category">
<option value="all">All</option>
<option value="General">General</option>
<option value="Work">Work</option>
<option value="Personal">Personal</option>
</select>
</label>
<input type="text" id="search" placeholder="Search tasks" />
</div>
<ul id="task-list"></ul>
</div>
CSS (optional for clarity)
<style>
#task-list li {
padding: 8px;
border: 1px solid #ccc;
margin-bottom: 4px;
cursor: move;
list-style: none;
}
#task-list li.completed {
text-decoration: line-through;
color: gray;
}
.category-label {
font-size: 0.8em;
color: #555;
margin-left: 8px;
}
</style>
JavaScript: Advanced Functionality
<script>
let tasks = [];
// Load and save tasks
function loadTasks() {
const stored = localStorage.getItem("tasks");
tasks = stored ? JSON.parse(stored) : [];
}
function saveTasks() {
localStorage.setItem("tasks", JSON.stringify(tasks));
}
const input = document.getElementById("task-input");
const categorySelect = document.getElementById("category-select");
const addBtn = document.getElementById("add-btn");
const taskList = document.getElementById("task-list");
const filterStatus = document.getElementById("filter-status");
const filterCategory = document.getElementById("filter-category");
const searchInput = document.getElementById("search");
// Render tasks
function renderTasks() {
taskList.innerHTML = "";
let filteredTasks = tasks;
// Filter by status
const status = filterStatus.value;
if (status === "completed") filteredTasks = filteredTasks.filter(t => t.completed);
else if (status === "pending") filteredTasks = filteredTasks.filter(t => !t.completed);
// Filter by category
const category = filterCategory.value;
if (category !== "all") filteredTasks = filteredTasks.filter(t => t.category === category);
// Search filter
const search = searchInput.value.toLowerCase();
if (search) filteredTasks = filteredTasks.filter(t => t.text.toLowerCase().includes(search));
filteredTasks.forEach((task, index) => {
const li = document.createElement("li");
li.textContent = task.text;
if (task.completed) li.classList.add("completed");
// Add category label
const span = document.createElement("span");
span.textContent = `[${task.category}]`;
span.className = "category-label";
li.appendChild(span);
li.draggable = true;
// Toggle completed
li.addEventListener("click", () => {
task.completed = !task.completed;
saveTasks();
renderTasks();
});
// Delete on double click
li.addEventListener("dblclick", () => {
const originalIndex = tasks.indexOf(task);
tasks.splice(originalIndex, 1);
saveTasks();
renderTasks();
});
// Drag-and-drop
li.addEventListener("dragstart", (e) => {
e.dataTransfer.setData("text/plain", tasks.indexOf(task));
});
li.addEventListener("dragover", (e) => e.preventDefault());
li.addEventListener("drop", (e) => {
e.preventDefault();
const draggedIndex = e.dataTransfer.getData("text");
const temp = tasks[draggedIndex];
tasks.splice(draggedIndex, 1);
const dropIndex = tasks.indexOf(task);
tasks.splice(dropIndex, 0, temp);
saveTasks();
renderTasks();
});
taskList.appendChild(li);
});
}
// Add task
addBtn.addEventListener("click", () => {
const text = input.value.trim();
const category = categorySelect.value;
if (!text) return;
tasks.push({ text, completed: false, category });
saveTasks();
input.value = "";
renderTasks();
});
// Filters and search
filterStatus.addEventListener("change", renderTasks);
filterCategory.addEventListener("change", renderTasks);
searchInput.addEventListener("input", renderTasks);
// Press Enter to add task
input.addEventListener("keypress", (e) => {
if (e.key === "Enter") addBtn.click();
});
// Initialize
loadTasks();
renderTasks();
</script>
Features Now Include:
- Add tasks with category → Work, Personal, or General
- Toggle completed → Click a task
- Delete tasks → Double-click a task
- Drag-and-drop reorder
- Sort/filter by status → Completed, Pending, All
- Filter by category
- Search tasks by text
- Persistent storage → Tasks remain on page reload
This version is now fully functional like a mini productivity tool. You can even expand it later with due dates, priorities, or color-coding.