🧠 Introduction
Looking to build your first real JavaScript project? A To-Do List App is the perfect place to start. It’s small, manageable, and teaches you key skills like DOM manipulation, event handling, and data persistence.
By the end of this beginner-friendly tutorial, you'll have built a working task tracker — right in your browser — with HTML, CSS, and vanilla JavaScript. No frameworks, no fluff. Just hands-on learning.
Let’s start building — snippet by snippet!
🧱 Step 1: Set Up Your HTML Structure
First, we’ll create the basic layout for our to-do app: an input box, a button to add tasks, and an empty list to display them.
🔹 HTML Snippet
<div class="todo-container">
<h1>My To-Do List</h1>
<input type="text" id="todo-input" placeholder="Enter a task..." />
<button id="add-btn">Add</button>
<ul id="todo-list"></ul>
</div>
✅ Explanation
- We wrap everything inside a
.todo-container
for styling purposes. - The
input
field lets users type in tasks. - The
button
triggers adding tasks to the list. - The
ul
(unordered list) will hold each task as a list item.
🎨 Step 2: Style the App with CSS
Let’s make it a bit more appealing with some basic CSS.
🔹 CSS Snippet
body {
margin: 0;
padding: 0;
background: linear-gradient(to right, #667eea, #764ba2);
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.todo-container {
background-color: #fff;
border-radius: 12px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
padding: 30px;
max-width: 400px;
width: 90%;
text-align: center;
}
.todo-container h1 {
margin-bottom: 20px;
color: #333;
}
#todo-input {
width: 70%;
padding: 10px 12px;
border: 1px solid #ccc;
border-radius: 6px;
font-size: 16px;
outline: none;
transition: border 0.3s ease;
}
#todo-input:focus {
border-color: #667eea;
}
#add-btn {
padding: 10px 16px;
margin-left: 10px;
background-color: #667eea;
border: none;
border-radius: 6px;
color: white;
font-weight: bold;
cursor: pointer;
transition: background-color 0.3s ease;
}
#add-btn:hover {
background-color: #556cd6;
}
#todo-list {
list-style: none;
padding: 0;
margin-top: 25px;
}
#todo-list li {
background-color: #f5f5f5;
padding: 12px 16px;
margin-bottom: 12px;
border-radius: 6px;
display: flex;
justify-content: space-between;
align-items: center;
transition: background-color 0.2s ease;
}
#todo-list li:hover {
background-color: #e0e0e0;
}
#todo-list button {
background-color: transparent;
border: none;
font-size: 18px;
cursor: pointer;
color: #ff5c5c;
transition: color 0.3s ease;
}
#todo-list button:hover {
color: #e60000;
}
✅ Explanation
- We center the app and set a clean font.
- Input and button spacing improves usability.
- The list is styled to remove bullet points and spacing.
🧠 Step 3: Add JavaScript to Handle Task Addition
Now let’s wire up the input and button to actually add tasks to the list.
🔹 JavaScript Snippet (Part 1)
const input = document.getElementById("todo-input");
const addBtn = document.getElementById("add-btn");
const list = document.getElementById("todo-list");
addBtn.addEventListener("click", () => {
const task = input.value.trim();
if (task) {
const li = document.createElement("li");
li.textContent = task;
list.appendChild(li);
input.value = "";
}
});
✅ Explanation
- We fetch references to the input, button, and list.
- On button click, we:
- Grab the input value
- Check if it's not empty
- Create a new
li
element - Add the task to the list
- Clear the input field
🗑️ Step 4: Add Delete Functionality
What good is a to-do list if you can’t remove tasks?
🔹 JavaScript Snippet (Part 2)
Update the addEventListener
function:
addBtn.addEventListener("click", () => {
const task = input.value.trim();
if (task) {
const li = document.createElement("li");
li.textContent = task;
const deleteBtn = document.createElement("button");
deleteBtn.textContent = "❌";
deleteBtn.style.marginLeft = "10px";
deleteBtn.addEventListener("click", () => {
li.remove();
});
li.appendChild(deleteBtn);
list.appendChild(li);
input.value = "";
}
});
✅ Explanation
- We create a "delete" button (❌ emoji style).
- When clicked, it removes the parent
li
from the DOM. - This keeps your task list clean and under control.
💾 Step 5: Save Tasks with LocalStorage
Want your to-dos to stick around even after refreshing? Let’s use localStorage
.
🔹 JavaScript Snippet (Part 3)
First, update your functions:
function saveTasks() {
const tasks = [];
list.querySelectorAll("li").forEach(li => {
const taskText = li.firstChild.textContent;
tasks.push(taskText);
});
localStorage.setItem("tasks", JSON.stringify(tasks));
}
Then, call saveTasks()
whenever tasks change:
deleteBtn.addEventListener("click", () => {
li.remove();
saveTasks();
});
addBtn.addEventListener("click", () => {
// ... rest of code
list.appendChild(li);
input.value = "";
saveTasks();
});
✅ Explanation
saveTasks()
grabs all task texts and stores them as a JSON string.- We call this function every time we add or remove a task.
🔁 Step 6: Load Tasks on Page Load
Finally, load saved tasks when the page loads.
🔹 JavaScript Snippet (Part 4)
function loadTasks() {
const tasks = JSON.parse(localStorage.getItem("tasks")) || [];
tasks.forEach(task => {
const li = document.createElement("li");
li.textContent = task;
const deleteBtn = document.createElement("button");
deleteBtn.textContent = "❌";
deleteBtn.style.marginLeft = "10px";
deleteBtn.addEventListener("click", () => {
li.remove();
saveTasks();
});
li.appendChild(deleteBtn);
list.appendChild(li);
});
}
window.addEventListener("DOMContentLoaded", loadTasks);
✅ Explanation
- On
DOMContentLoaded
, we load saved tasks. - Each is rendered just like when we add them manually.
🔧 Gentle Reminder
Once you're done following the steps above, go ahead and combine all the snippets into your HTML, CSS, and JS files. You’ll see your fully working to-do list app right in the browser. 🎉
✅ Best Practices & Tips
- Use class names instead of inline styles for larger projects.
- Consider accessibility (e.g., adding
aria-labels
or semantic HTML). - Add task validation (e.g., prevent duplicate entries).
- Use keyboard shortcuts (e.g., add task on "Enter" key press).
🔍 SEO & Performance Tie-In
A fast-loading, minimal JavaScript app like this can improve site speed and user engagement, which are key factors in SEO rankings. Clean, interactive features also reduce bounce rate and increase time-on-page.
📌 Conclusion
You just built a fully functional to-do list app using pure JavaScript — and picked up critical skills in DOM manipulation, event handling, and local storage.
💬 Got questions or ideas? Leave a comment below. 🚀 Want more? Check out our other tutorials on JavaScript basics, project building, and web automation!
0 Comments