DEV Community

Cover image for My Trainer Laid the Bricks. I Had to Build the House. Here Is What Happened.
Ebenezer
Ebenezer

Posted on

My Trainer Laid the Bricks. I Had to Build the House. Here Is What Happened.

Vijayaragavan sir did not hand me a tutorial link this time.

He sat down, explained the logic of a to-do list in plain words, told me what it should do, and then stood up. That was it. Just a starter code. Just the idea — and the expectation that I would figure out the rest.

He laid the bricks. I had to complete the house.

I have heard that phrase before. It never meant anything to me until I was sitting alone with a blank JavaScript file, knowing what I wanted the app to do and having no clear idea how to make the page actually respond to me typing something and pressing a button.

Here is what we are going to cover:

  • How DOM manipulation is the thing nobody explains properly until you need it
  • How span tags turned a flat list item into something that actually works
  • How event listeners think — and why stopPropagation saved me from a bug I could not name

1. DOM Manipulation — When the Page Finally Talks Back

I had read the phrase "DOM manipulation" in at least a dozen tutorials before this project. Every one of them explained what the DOM is. None of them made me feel it.

DOM manipulation is the act of using JavaScript to reach into the HTML of a page and change it — add something, remove something, update something — while the user is looking at it. No refresh. No reload. The page just responds.

The moment it clicked for me was when I wrote this:

// Create a new list item and add it to the page when the user clicks "Add"
function newElement() {
    const input = document.getElementById("content");
    const inputVal = input.value.trim();

    if (inputVal === "") {
        alert("You must write some to-do");
        return;
    }

    const list = document.createElement("li");
    const myList = document.getElementById("myList");
    myList.appendChild(list);

    input.value = "";
}
Enter fullscreen mode Exit fullscreen mode

What just happened: document.getElementById("content") reaches into the HTML and grabs the input field by its id. input.value.trim() reads whatever the user typed and removes any extra spaces around it. document.createElement("li") builds a brand new list item in memory — it does not exist on the page yet. myList.appendChild(list) actually puts it on the page. Then input.value = "" clears the input so the user can type the next task.

That sequence — grab, create, append, clear — is the backbone of almost every interactive thing you will build in JavaScript. I did not understand why tutorials kept repeating it until I wrote it myself and watched a new item appear on screen the moment I pressed Add.

It felt like the page was alive.

I also set the heading text only after the first task is added:

// Show "Today's Agenda" heading the moment the first task appears
const heading = document.getElementById("text");
heading.textContent = "Today's Agenda";
Enter fullscreen mode Exit fullscreen mode

What just happened: textContent sets the visible text of any element. Before the user adds anything, the heading is empty. After the first task, it appears. One line. No conditions beyond the ones already in place.


2. Span Tags — The Hidden Architecture Inside Each Task

This is the part I did not anticipate needing to think about.

Each task on the list is not just text. It has three parts: a custom checkbox on the left, the task text in the middle, and a delete button on the right. A plain <li> element cannot hold all three independently unless you structure what is inside it carefully.

That is what span tags are for. A span is an inline container — it holds content inside a larger element without breaking the layout. On their own, spans are invisible. But give them a class, style them with CSS, and attach behavior to them, and they become the working parts of your UI.

// Build the internal structure of each task item using two span elements
const list = document.createElement("li");

const textSpan = document.createElement("span");
textSpan.classList.add("task-text");
textSpan.textContent = inputVal;

const spanClose = document.createElement("span");
spanClose.classList.add("close");
spanClose.innerHTML = "&times;";

list.appendChild(textSpan);
list.appendChild(spanClose);
Enter fullscreen mode Exit fullscreen mode

What just happened: two spans are created in memory. The first gets a class of task-text and holds the user's input. The second gets a class of close and holds the times symbol, which renders as an X on screen. Both are appended to the list item in order — text first, delete button second. CSS does the rest: flex-layout lines them up, and the close span gets pushed to the right with margin-left: auto.

Before I understood this, I tried putting everything directly into the li as one piece of text. The delete button worked but toggling the checkbox would also trigger the delete. They were tangled. Separating them into spans gave each piece of the UI its own identity — its own class, its own event, its own behavior.

That separation is not just a coding pattern. It is a way of thinking about UI. Every visible thing on a page is its own element, and elements can be nested, layered, and controlled independently.


3. Event Listeners — And the One Line That Saved Everything

The toggle worked. The delete worked. But when I clicked delete, the task also toggled its checked state before disappearing. A flash of green, then gone. It looked broken.

I did not know what was causing it.

The problem is called event bubbling. When you click an element, the browser does not just fire the event on that element — it fires it on every parent element above it too, all the way up the tree. So clicking the close span was also triggering the click event on the li above it, which toggled the checked class before the delete had time to hide the item.

One line fixed it:

// Toggle checked state only when clicking the text — not the delete button
list.addEventListener("click", function (ev) {
    if (ev.target.tagName === "LI" || ev.target.classList.contains("task-text")) {
        list.classList.toggle("checked");
    }
});

spanClose.addEventListener("click", function (e) {
    e.stopPropagation();   // Stop the click from reaching the li above
    list.style.display = "none";
});
Enter fullscreen mode Exit fullscreen mode

What just happened: the list click listener only toggles checked when the click target is the li itself or the task text span — not the close button. e.stopPropagation() on the close span tells the browser to stop passing this click event upward. It ends here. The li never hears about it.

stopPropagation is one of those things that sounds abstract until the exact bug it solves shows up in front of you. I had the bug first. Then I found the fix. Then it made sense. That order — problem, fix, understanding — is how most of JavaScript actually gets learned.

One more thing I want to be honest about: during this project, I learned that local storage exists. It is a browser feature that lets you save data between sessions — so tasks would survive a page refresh. I did not implement it in this version. Not because it was too hard, but because I wanted to make the core logic work cleanly first. Local storage is the next version. Skipping it was a decision, not a failure.


What This Project Actually Taught Me

You now know that a to-do list is not a list. It is a DOM tree that responds to user input in real time, where every visible element is a node you can create, modify, and remove with JavaScript. That sounds obvious in hindsight. Before this project, it was not.

My trainer gave me the logic. I had to translate it into working code. That gap — between understanding what something should do and knowing how to make it do it — is where the real learning happens. No tutorial closes that gap for you. You have to walk across it yourself.

You can see the finished app here: https://ebenezer6374-arch.github.io/ToDo/

If you want to see how the code is structured, the repository is on my GitHub. Everything from the HTML layout to the event logic is there — no frameworks, no libraries, just plain JavaScript doing the work.

Open a new file right now and try building this from scratch without looking at any code. Just hold the idea in your head — input, add button, list that grows, items that can be checked and deleted — and see how far you get before you need to look something up. You will be surprised.

What part of DOM manipulation confused you the most when you first ran into it? Drop it in the comments. I read every one, and I am probably one week ahead of where you are right now.


Top comments (0)