Build a Todo List Website
Abhishek Gurjar
Posted on August 21, 2024
Introduction
Hello, developers! I'm thrilled to introduce my latest project: a Todo List application. This project is perfect for anyone looking to improve their JavaScript skills by working on a practical and widely-used tool. Whether you're just starting out or looking to refine your skills, building a Todo List is a great way to learn about handling user input, managing data, and dynamically updating the DOM.
Project Overview
The Todo List application is a simple yet powerful tool that allows users to manage their tasks efficiently. It features an intuitive interface where users can add, edit, and delete tasks, mark them as completed, and filter tasks based on their status. This project is a great way to understand the core concepts of web development, including event handling and data persistence using localStorage.
Features
- User-Friendly Interface: A clean and intuitive design that makes task management easy.
- Add, Edit, and Delete Tasks: Fully functional controls to manage your tasks effectively.
- Mark Tasks as Completed: Easily mark tasks as completed and filter them based on their status.
- Persistent Data: All tasks are stored in localStorage, so your list remains intact even after refreshing the page.
- Responsive Design: The layout is responsive and works seamlessly on both desktop and mobile devices.
Technologies Used
- HTML: Structures the web page and input elements.
- CSS: Styles the interface to provide a user-friendly experience.
- JavaScript: Handles the logic for adding, editing, deleting, and filtering tasks, as well as managing data in localStorage.
Project Structure
Here's a quick overview of the project structure:
Todo-List/
├── index.html
├── styles.css
└── script.js
- index.html: Contains the HTML structure for the Todo List application.
- styles.css: Includes CSS styles to enhance the appearance and responsiveness of the Todo List.
- script.js: Manages the application logic, including task management and localStorage operations.
Installation
To get started with the Todo List project, follow these steps:
-
Clone the repository:
git clone https://github.com/abhishekgurjar-in/Todo-List.git
-
Open the project directory:
cd Todo-List
-
Run the project:
- Open the
index.html
file in your web browser to start using the Todo List application.
- Open the
Usage
- Open the website in a web browser.
- Add a task by typing in the input field and pressing "Enter."
- Edit or delete tasks using the provided options.
- Mark tasks as completed by checking the corresponding checkbox.
- Filter tasks by their status using the filter options at the top of the list.
- Clear all tasks using the "Clear All" button to start fresh.
Code Explanation
HTML
The index.html
file provides the structure for the Todo List application, including the input field for adding tasks, buttons for filtering tasks, and a list to display the tasks. Here’s a brief overview:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link
rel="stylesheet"
href="https://unicons.iconscout.com/release/v4.0.0/css/line.css"
/>
<link rel="stylesheet" href="style.css" />
<script src="script.js" defer></script>
<title>ToDo</title>
</head>
<body>
<div class="main">
<div id="logo">
<img
src="./logo/175e8acd1b69064c1fafe52ed5b12019-removebg-preview.png"
alt=""
/>
</div>
<div class="wrapper">
<div class="task-input">
<input type="text" placeholder="Add a new task" />
</div>
<div class="controls">
<div class="filters">
<span id="all" class="active">All</span>
<span id="pending">Pending</span>
<span id="completed">Completed</span>
</div>
<button class="clear-btn">Clear All</button>
</div>
<ul class="task-box"></ul>
</div>
</div>
<div class="footer">
<p>Made with ❤️ by Abhishek Gurjar</p>
</div>
</body>
</html>
CSS
The styles.css
file styles the Todo List application, ensuring a clean and responsive design. Here are some key styles:
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap");
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Poppins", sans-serif;
}
body {
background: #fff;
}
.main {
min-height: 85vh;
}
#logo {
width: 100%;
height: 7vh;
display: flex;
align-items: bottom;
justify-content: center;
}
img {
width: 300px;
height: 222px;
}
::selection {
color: #fff;
background: #1e293b;
}
.wrapper {
max-width: 405px;
background: #64d1ef;
margin: 137px auto;
padding: 28px 0 30px;
border-radius: 7px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.401);
}
.task-input {
height: 52px;
padding: 0 25px;
position: relative;
}
.task-input input {
height: 100%;
width: 100%;
outline: none;
font-size: 18px;
border-radius: 5px;
padding: 0 20px 0 10px;
border: 1px solid #7a7a7a;
}
.task-input input:focus,
.task-input input.active {
padding-left: 10px;
border: 2px solid #1e293b;
}
.task-input input::placeholder {
color: #bfbfbf;
}
.controls,
li {
display: flex;
align-items: center;
justify-content: space-between;
}
.controls {
padding: 18px 25px;
border-bottom: 1px solid #000000a4;
}
.filters span {
margin: 0 8px;
font-size: 17px;
color: #444;
cursor: pointer;
}
.filters span:first-child {
margin-left: 0;
}
.filters span.active {
color: #101216;
}
.controls .clear-btn {
border: none;
opacity: 0.6;
outline: none;
color: #fff;
cursor: pointer;
font-size: 13px;
padding: 7px 13px;
border-radius: 4px;
background: #1e293b;
letter-spacing: 0.3px;
pointer-events: none;
transition: transform 0.25s ease;
}
.clear-btn.active {
opacity: 0.9;
pointer-events: auto;
}
.clear-btn:active {
transform: scale(0.93);
}
.task-box {
margin-top: 20px;
margin-right: 5px;
padding: 0 20px 10px 25px;
}
.task-box.overflow {
overflow-y: auto;
max-height: 300px;
}
.task-box::-webkit-scrollbar {
width: 5px;
}
.task-box::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 25px;
}
.task-box::-webkit-scrollbar-thumb {
background: #e6e6e6;
border-radius: 25px;
}
.task-box .task {
list-style: none;
font-size: 17px;
margin-bottom: 18px;
padding-bottom: 16px;
align-items: flex-start;
border-bottom: 1px solid #2c2a2a;
}
.task-box .task:last-child {
margin-bottom: 0;
border-bottom: 0;
padding-bottom: 0;
}
.task-box .task label {
display: flex;
align-items: flex-start;
}
.task-box label input {
margin-top: 7px;
accent-color: #1e293b;
}
.task-box label p {
user-select: none;
margin-left: 12px;
word-wrap: break-word;
}
.task label p.checked {
text-decoration: line-through;
}
.task-box .settings {
position: relative;
}
.settings :where(i, li) {
cursor: pointer;
}
.settings .task-menu {
z-index: 10;
right: -5px;
bottom: -65px;
padding: 5px 0;
background: #fff;
position: absolute;
border-radius: 4px;
transform: scale(0);
transform-origin: top right;
box-shadow: 0 0 6px rgba(0, 0, 0, 0.15);
transition: transform 0.2s ease;
}
.task-box .task:last-child .task-menu {
bottom: 0;
transform-origin: bottom right;
}
.task-box .task:first-child .task-menu {
bottom: -65px;
transform-origin: top right;
}
.task-menu.show {
transform: scale(1);
}
.task-menu li {
height: 25px;
font-size: 16px;
margin-bottom: 2px;
padding: 17px 15px;
cursor: pointer;
justify-content: flex-start;
}
.task-menu li:last-child {
margin-bottom: 0;
}
.settings li:hover {
background: #f5f5f5;
}
.settings li i {
padding-right: 8px;
}
.footer {
text-align: center;
margin: 40px;
}
@media (max-width: 400px) {
body {
padding: 0 10px;
}
.wrapper {
padding: 20px 0;
}
.filters span {
margin: 0 5px;
}
.task-input {
padding: 0 20px;
}
.controls {
padding: 18px 20px;
}
.task-box {
margin-top: 20px;
margin-right: 5px;
padding: 0 15px 10px 20px;
}
.task label input {
margin-top: 4px;
}
}
JavaScript
The script.js
file contains the logic for adding, editing, deleting, and filtering tasks. Here's an overview of the main functions:
const taskInput = document.querySelector(".task-input input"),
filters = document.querySelectorAll(".filters span"),
clearAll = document.querySelector(".clear-btn"),
taskBox = document.querySelector(".task-box");
let editId,
isEditTask = false,
todos = JSON.parse(localStorage.getItem("todo-list"));
// Filter tasks based on status (all, completed, pending)
filters.forEach(btn => {
btn.addEventListener("click", () => {
document.querySelector("span.active").classList.remove("active");
btn.classList.add("active");
showTodo(btn.id);
});
});
function showTodo(filter) {
let liTag = "";
if (todos) {
todos.forEach((todo, id) => {
let completed = todo.status == "completed" ? "checked" : "";
if (filter == todo.status || filter == "all") {
liTag += `<li class="task">
<label for="${id}">
<input onclick="updateStatus(this)" type="checkbox" id="${id}" ${completed}>
<p class="${completed}">${todo.name}</p>
</label>
<div class="settings">
<i onclick="showMenu(this)" class="uil uil-ellipsis-h"></i>
<ul class="task-menu">
<li onclick='editTask(${id}, "${todo.name}")'><i class="uil uil-pen"></i>Edit</li>
<li onclick='deleteTask(${id}, "${filter}")'><i class="uil uil-trash"></i>Delete</li>
</ul>
</div>
</li>`;
}
});
}
taskBox.innerHTML = liTag || `<span>You don't have any task here</span>`;
let checkTask = taskBox.querySelectorAll(".task");
!checkTask.length ? clearAll.classList.remove("active") : clearAll.classList.add("active");
taskBox.offsetHeight >= 300 ? taskBox.classList.add("overflow") : taskBox.classList.remove("overflow");
}
showTodo("all"); // Show all tasks by default
// Function to show the menu for task options
function showMenu(selectedTask) {
let menuDiv = selectedTask.parentElement.lastElementChild;
menuDiv.classList.add("show");
document.addEventListener("click", e => {
if (e.target.tagName != "I" || e.target != selectedTask) {
menuDiv.classList.remove("show");
}
});
}
// Function to update the status of a task (completed or pending)
function updateStatus(selectedTask) {
let taskName = selectedTask.parentElement.lastElementChild;
if (selectedTask.checked) {
taskName.classList.add("checked");
todos[selectedTask.id].status = "completed";
} else {
taskName.classList.remove("checked");
todos[selectedTask.id].status = "pending";
}
localStorage.setItem("todo-list", JSON.stringify(todos));
}
// Function to edit an existing task
function editTask(taskId, textName) {
editId = taskId;
isEditTask = true;
taskInput.value = textName;
taskInput.focus();
taskInput.classList.add("active");
}
// Function to delete a task
function deleteTask(deleteId, filter) {
isEditTask = false;
todos.splice(deleteId, 1);
localStorage.setItem("todo-list", JSON.stringify(todos));
showTodo(filter);
}
// Clear all tasks
clearAll.addEventListener("click", () => {
isEditTask = false;
todos.splice(0, todos.length);
localStorage.setItem("todo-list", JSON.stringify(todos));
showTodo();
});
// Add a new task or update an existing one
taskInput.addEventListener("keyup", e => {
let userTask = taskInput.value.trim();
if (e.key == "Enter" && userTask) {
if (!isEditTask) {
todos = !todos ? [] : todos;
let taskInfo = { name: userTask, status: "pending" };
todos.push(taskInfo);
} else {
isEditTask = false;
todos[editId].name = userTask;
}
taskInput.value = "";
localStorage.setItem("todo-list", JSON.stringify(todos));
showTodo(document.querySelector("span.active").id);
}
});
Live Demo
Check out the live demo of the Todo List application here.
Conclusion
Building this Todo List application was an insightful experience, allowing me to deepen my understanding of JavaScript, DOM manipulation, and data persistence. I hope this project serves as an inspiration for you to create your own task management tools. Happy coding!
Credits
This project was developed as part of my ongoing efforts to master web development, focusing on practical applications that enhance everyday productivity.
Author
- Abhishek Gurjar
Posted on August 21, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.