DEV Community

Cover image for I Built a Simple Todo App (and Learned More Than I Expected!)
Kathirvel S
Kathirvel S

Posted on

I Built a Simple Todo App (and Learned More Than I Expected!)

I built a simple Todo List App using JavaScript — something that keeps track of tasks quietly in the background, ready whenever I need it.

👉 You can try it here:

Click Here to Try

This blog isn’t just about what I built — it’s about how it works, especially the JavaScript behind it. I’ll walk you through it in a way that actually makes sense, even if you’re just getting started.


What This Todo App Can Do

Here’s what the app handles:

  • Add new tasks
  • Edit existing tasks
  • Delete tasks
  • Mark tasks as completed
  • Save everything using localStorage (even after refresh)

It’s simple, but it covers the core ideas every beginner needs to understand.


Where Everything Lives and Stays

let todos = JSON.parse(localStorage.getItem("vals")) || [];
Enter fullscreen mode Exit fullscreen mode

What’s happening here?

  • The app checks if there are already saved tasks in localStorage
  • If yes, it converts them back into a usable array
  • If not, it starts fresh with an empty list

Think of localStorage as a small shelf where your tasks stay even when you leave and come back.


Locking Things in Place

function savedata() {
    localStorage.setItem("vals", JSON.stringify(todos));
}
Enter fullscreen mode Exit fullscreen mode

This function makes sure your tasks don’t disappear.

  • It converts the array into a string
  • Then stores it in the browser

Since localStorage only understands text, we use JSON.stringify() to make it readable.


Rebuilding the Screen Every Time

function renderTodos() {
    logg.innerHTML = "";
Enter fullscreen mode Exit fullscreen mode

Before showing anything, the app clears what’s already on the screen.

Why?
Because instead of updating one item at a time, it rebuilds the entire list cleanly every time something changes.


Going Through Each Task

todos.forEach((todo, index) => {
Enter fullscreen mode Exit fullscreen mode

This loop goes through every task and creates its visual representation.


Showing the Task

const span = document.createElement("span");
span.innerText = todo.text;
Enter fullscreen mode Exit fullscreen mode

Each task is placed inside a span element.


Marking It Done

if (todo.done) span.classList.add("done");
Enter fullscreen mode Exit fullscreen mode

If a task is completed, it gets a line-through style.


Toggling Completion

span.addEventListener("click", () => {
    todo.done = !todo.done;
    savedata();
    renderTodos();
});
Enter fullscreen mode Exit fullscreen mode

Clicking on a task flips its state:

  • If it was incomplete, it becomes complete
  • If it was complete, it goes back

Then the app saves and refreshes everything.


Editing a Task

edit.addEventListener("click", () => {
    userinput.value = todo.text;
    editIndex = index;
    btn.innerText = "Update";
});
Enter fullscreen mode Exit fullscreen mode

When you click edit:

  • The task text is placed back into the input field
  • The app remembers which task you’re editing
  • The button changes from "Add" to "Update"

Removing a Task

del.addEventListener("click", () => {
    todos.splice(index, 1);
    savedata();
    renderTodos();
});
Enter fullscreen mode Exit fullscreen mode

This removes the selected task from the list, updates storage, and refreshes the display.


One Function Handling Two Jobs

function handleTask() {
Enter fullscreen mode Exit fullscreen mode

This function decides whether to add a new task or update an existing one.


Avoiding Empty Entries

if (val === "") return;
Enter fullscreen mode Exit fullscreen mode

No empty tasks are allowed.


Adding a New Task

todos.push({ text: val, done: false });
Enter fullscreen mode Exit fullscreen mode

A new task is added as an object with two properties:

  • text
  • done

Updating an Existing Task

todos[editIndex].text = val;
editIndex = -1;
btn.innerText = "Add";
Enter fullscreen mode Exit fullscreen mode

The selected task is updated, and the app exits edit mode.


Final Steps

userinput.value = "";
savedata();
renderTodos();
Enter fullscreen mode Exit fullscreen mode

After any change:

  • The input is cleared
  • Data is saved
  • The UI is refreshed

A Small but Important Interaction

userinput.addEventListener("keypress", (e) => {
    if (e.key === "Enter") {
        handleTask();
    }
});
Enter fullscreen mode Exit fullscreen mode

Pressing Enter triggers the same action as clicking the button.
This small addition makes the app feel much smoother to use.


What This Project Really Teaches

Even though the app looks simple, it introduces key concepts:

  • How to work with the DOM
  • How to handle user events
  • How to manage data using arrays and objects
  • How to store data in the browser
  • How to keep UI and data in sync

These are the exact building blocks used in larger applications.


Why Building Something Like This Matters

Watching tutorials can only take you so far.

When you build something yourself:

  • You start understanding why things work
  • You make mistakes and learn from them
  • You begin thinking like a developer

This is where real progress happens.


What Would You Add Next?

If you want to take this further, you could try:

  • Adding due dates
  • Setting priorities
  • Creating a dark mode
  • Improving animations

Each improvement adds another layer of understanding.


Final Thoughts

If you’ve read this far, you’re not just scrolling — you’re trying to understand.

And that matters.

Every line of code in this project represents a step forward. Not just in JavaScript, but in how you approach problems, break them down, and build solutions from scratch.

So here’s something to think about:

What’s the next small idea you’ve been putting off building?

Start it. Keep it simple. Let it grow.

And when you do, you’ll realize this wasn’t just about a todo app — it was about learning how to turn ideas into something real.

Top comments (0)