DEV Community

Cover image for To-Do List (Not A Guide – Just Me Trying To Make One)
Danny
Danny

Posted on • Updated on

To-Do List (Not A Guide – Just Me Trying To Make One)

01/02/2021

Introduction

This is not a tutorial on how to make a to-do list app. This is just a code newbie typing out their thought process as he may or may not descend into madness while trying to make one.

Thought Stream

First I’ll need to set up a form in the HTML so that I’ll be able to type what I want to add to the to-do list:

    <div class="container">
      <h1>To-Do List</h1>
      <form>
        <label for="entry">Add something to the to-do list:</label><br /><br />
        <input ="textid="input" name="entry" />
        <button type="submit" id="add">Add</button>
      </form>
      <ul id="todoListID" class="todoList"></ul>
    </div>
Enter fullscreen mode Exit fullscreen mode

Blurry image of the start of the to-do list

Next up is to find out why that screenshot that I just copy and pasted into LibreOffice Writer is so blurry. After some Googling it looks like you need to use an image editor to remove the alpha channel/transparency and then when you copy and paste it into Writer it won’t be blurry. I’m not going to do that. I’ll just save them in Microsoft Paint instead of this document as I’ll need to upload them to the blog separately anyway.

Non blurry image of the start of the to-do list


Now it’ll need some JavaScript to make it functional. I’ve added comments to every line to save me explaining it here:

const todoListID = document.getElementById("todoListID"); // gets the <ul> "To-Do List"
const add = document.getElementById("add"); // gets the "Add" button

add.addEventListener("click", function (event) {
  event.preventDefault(); // prevent the default action of the button from submitting the form
  let input = document.getElementById("input"); // gets the input field
  let newEntry = document.createElement("li"); // creates a new list item
  newEntry.innerText = input.value; // sets the new list item text to the same value as the input field
  todoListID.appendChild(newEntry); // adds the new list item to the unordered list in the HTML (the to-do list)
});
Enter fullscreen mode Exit fullscreen mode

To-do list with test items

Good stuff.


Now the items will need buttons so that items can be crossed off or removed from the list:

newEntry.innerText = input.value;

becomes:

newEntry.innerHTML = input.value + '<button>hello</button>';

I changed “innerText” to “innerHTML” so that I can include a button alongside the item. Let’s see if it worked:

To-do list with test items with buttons

Close enough. Aligning the button to the right hand side so that it isn’t so close to the text will probably be a nightmare so I’ll leave it like that for now.


Next I’ll need some more JavaScript to make the button work but I’m sensing danger. If I give the button an ID so that I can use an eventListener on it, I’ll probably get an error because the button doesn’t exist when the page loads, it only exists after the user has added an item:

add.addEventListener("click", function (event) {
  event.preventDefault();
  let input = document.getElementById("input");
  let newEntry = document.createElement("li");
  newEntry.innerHTML = input.value + '<button id="test">hello</button>'; // here I have added an ID to the button so that I can hopefully add an eventListener to it
  todoListID.appendChild(newEntry);
});

const test = document.getElementById("test");
test.addEventListener("click", function () {
  console.log("testing");
});
Enter fullscreen mode Exit fullscreen mode

Uncaught TypeError: test is null
http://127.0.0.1:5500/main.js:16

Yep. Maybe if I put the new eventListener inside the other eventListener it will work?

add.addEventListener("click", function (event) {
  event.preventDefault();
  let input = document.getElementById("input");
  let newEntry = document.createElement("li");
  newEntry.innerHTML = input.value + '<button id="test">hello</button>'; // here I have added an ID to the button so that I can hopefully add an eventListener to it
  const test = document.getElementById("test");
  test.addEventListener("click", function () {
    console.log("testing");
  });
  todoListID.appendChild(newEntry);
});
Enter fullscreen mode Exit fullscreen mode

Nope.

Uncaught TypeError: test is null
http://127.0.0.1:5500/main.js:12
EventListener.handleEvent* http://127.0.0.1:5500/main.js:4

In the past I’ve seen setAttribute() used to set IDs on buttons so maybe I’ll give that a shot:

let delete = document.createElement("button");
Enter fullscreen mode Exit fullscreen mode

Wait. Why won’t it let me do that? Oh, “delete” is a reserved word. Try again:

add.addEventListener("click", function (event) {
  event.preventDefault();
  let input = document.getElementById("input");
  let newEntry = document.createElement("li");
  newEntry.innerHTML = input.value;
  let deleteEntry = document.createElement("button");
  deleteEntry.setAttribute("id", "delete");
  newEntry.appendChild(deleteEntry);
  todoListID.appendChild(newEntry);
});
Enter fullscreen mode Exit fullscreen mode

To-do list with strange buttons

Looks like I’m going way off course here. I’ll just try onclick() even though I’ve read it’s bad practice. It can’t be that bad if it works though:

  newEntry.innerHTML = input.value + '<button id="test" onclick="testing()">hello</button>';

function testing() {
  console.log("test");
}
Enter fullscreen mode Exit fullscreen mode

test

Perfect! Now to rename all the “test” and “testing” so it looks like I never doubted myself. And so that they’ll be more descriptive. But mainly the first thing.

newEntry.innerHTML =
    input.value +
    '<button id="deleteBtn" onclick="deleteEntry()">Delete</button>';

function deleteEntry() {
  console.log("deleted");
}
Enter fullscreen mode Exit fullscreen mode

Next will be to try to make it actually delete the entry. I’m thinking if I can get the target element that was clicked, then I can go from there:

function deleteEntry(event) {
  console.log(event.target);
}
Enter fullscreen mode Exit fullscreen mode

Uncaught TypeError: event is undefined
deleteEntry http://127.0.0.1:5500/main.js:26
onclick http://127.0.0.1:5500/index.html?:1

Maybe not. Why’s that not working? Oh, I guess that the onclick() needs “event” too.

  newEntry.innerHTML =
    input.value +
    '<button id="deleteBtn" onclick="deleteEntry(event)">Delete</button>';
Enter fullscreen mode Exit fullscreen mode

Wahey! Success.

<button id="deleteBtn" onclick="deleteEntry(event)"> 
Enter fullscreen mode Exit fullscreen mode

I wonder why that worked though? I guess “event” is the argument when the function is called? So if I change the “event” parameters in the deleteEntry() function to make it less confusing, it should still work:

function deleteEntry(a) {
  console.log(a.target);
}


<button id="deleteBtn" onclick="deleteEntry(event)"> 
Enter fullscreen mode Exit fullscreen mode

OK. But I still don’t know why “event” is defined. Oh maybe it’s from the eventListener:

add.addEventListener("click", function (event) {
  newEntry.innerHTML =
    input.value +
    '<button id="deleteBtn" onclick="deleteEntry(event)">Delete</button>';
});
Enter fullscreen mode Exit fullscreen mode

But if it is, then I have no idea what’s happening. Isn’t this eventListener triggered by clicking the “Add” button?

Should I even post this blog? Anyone that reads it will see that I’m a dummy.

Well you’ve already spent 3 hours on it and the to-do list so you might as well. Someone might explain things to you if you’re lucky. And anyone that likes you enough to even read this blog post probably already knows that you’re a dummy anyway.

Good points. Let’s carry on then.


Where were we? Oh yeah, the delete button. It’s working but we don’t really know why. No need to let that stop us though. Time to see if we can get it to actually delete the entry:

function deleteEntry(a) {
  a.target.parentNode.remove();
}
Enter fullscreen mode Exit fullscreen mode

Great success!

Now to add a strikeout effect to completed items. I can’t be bothered dealing with buttons again so I’ll just try to use an eventListener on the list:

todoListID.addEventListener("click,", function (event) {
  console.log("aaaaaaaaaa");
  console.log(event.target);
});
Enter fullscreen mode Exit fullscreen mode

This doesn’t log anything at all in the console when clicking on the to-do list for some reason. Maybe I’ll be lucky and VSCode’s Live Server extension has just stopped working. I’ll restart it and see.

Nope, unfortunately it was working fine. I’ll make a new list to test it. One that already exists on the page:

To-do list with a separate test list on the page

const toDoListID2 = document.getElementById("toDoListID2");

todoListID2.addEventListener("click,", function (event) {
Enter fullscreen mode Exit fullscreen mode

Nope

todoListID2.children.addEventListener("click,", function (event) {
Enter fullscreen mode Exit fullscreen mode

Nothing

todoListID2.children[1].addEventListener("click,", function (event) {
Enter fullscreen mode Exit fullscreen mode

Nada

20 minutes later

Still no progress. Let’s just copy some code from the internet then. Found this on www.w3schools.com:

var list = document.querySelector("ul");
list.addEventListener(
  "click",
  function (ev) {
    if (ev.target.tagName === "LI") {
      //   ev.target.classList.toggle("checked");
      console.log("aaaaaa");
    }
  },
  false
);
Enter fullscreen mode Exit fullscreen mode

Still nothing happens. Oh I think querySelector() only returns the first result. So it won’t work on my test list but might work on the real one:

aaaaaa

Let’s see if we can get it working on the test list then. Oh no. Is that a typo?

const toDoListID2 = document.getElementById("toDoListID2");

todoListID2.addEventListener("click,", function (event) {
Enter fullscreen mode Exit fullscreen mode

The ID in the HTML is:

<ul id="todoListID2" class="todoList2">
Enter fullscreen mode Exit fullscreen mode

Disaster. Let’s change that because it’s probably case sensitive and see if it fixes anything. Nope. Is that a good thing or a bad thing? I guess good because now we can just use that code from w3schools. Maybe querySelector() gives us something different to getElementById()?

const todoListID2 = document.getElementById("todoListID2");
console.log(todoListID2);
Enter fullscreen mode Exit fullscreen mode

< ul id="todoListID2" class="todoList2">

var list = document.querySelector("ul");
console.log(list);
Enter fullscreen mode Exit fullscreen mode

< ul id="todoListID" class="todoList">

Nope. Same type of results. Let’s see if the w3schools code works with getElementById() instead of querySelector():

todoListID2.addEventListener(
  "click",
  function (ev) {
    if (ev.target.tagName === "LI") {
      //   ev.target.classList.toggle("checked");
      console.log("aaaaaa");
    }
  },
  false
);
Enter fullscreen mode Exit fullscreen mode

aaaaaa

Yep that works. So it must be my code that doesn’t work. Who could have guessed that??? Let’s try it with their code then:

const todoListID2 = document.getElementById("todoListID2");
console.log(todoListID2);
todoListID2.addEventListener("click,", function (event) {
  if (event.target.tagName === "LI") {
    console.log("aaaaaaaaaa");
  }
});
Enter fullscreen mode Exit fullscreen mode

Still doesn’t work. The only other thing I see that’s different is the “false” parameter:

const todoListID2 = document.getElementById("todoListID2");
console.log(todoListID2);
todoListID2.addEventListener(
  "click,",
  function (event) {
    if (event.target.tagName === "LI") {
      console.log("aaaaaaaaaa");
    }
  },
  false
);
Enter fullscreen mode Exit fullscreen mode

Still doesn’t work. What the heck is happening here? I’ve had enough of this. I’m taking a break.


OK I’m back. Oh I forgot how stuck I was. Whatever, let’s just do it the w3schools way because for some reason that’s the only way it works:

let list = document.querySelector("ul");
list.addEventListener(
  "click",
  function (ev) {
    if (ev.target.tagName === "LI") {
      ev.target.classList.toggle("clicked");
    }
  },
  false
);
Enter fullscreen mode Exit fullscreen mode

To-do list strikethrough

Lovely jubbly.


What’s next? Style it nicely or try to move the delete button to the right hand side? I’ll try the delete button. I should probably remake the container and use flex or grid to position the button which might make it more responsive but I’m fed up. So I’m just going to add position: relative to the li and then this to the "Delete" button:

.delete {
  position: absolute;
  right: 0;
}
Enter fullscreen mode Exit fullscreen mode

To-do list buttons on the right hand side


Now just need to add some styling and get the heck out of here. Removing the bullet points and changing colour on every other item always makes lists easier to read:

li:nth-child(even) {
  background-color: red;
}
Enter fullscreen mode Exit fullscreen mode

To-do list with the even rows in red

But now the background colour doesn’t change when I click on an even item, only the text does:

To-do list items background problem

So should I try to find out where the CSS specificity is going wrong or just add !important? That’s an easy choice:

.clicked {
  background: rgb(255, 0, 179) !important;
  color: rgb(0, 255, 42);
  text-decoration: line-through;
}
Enter fullscreen mode Exit fullscreen mode

To-do list items background problem fixed

Now to pick some nicer colours from the internet and space things out a bit. The end.


Me again. It’s a new day. Patience has been refilled and there’s problems to fix. It’s possible to click the “Add” button without any text in the input field, meaning it adds an empty entry to the list. Which I’m fine with. Doing nothing is one of my favourite things to do so I’m not going to disallow it with JS. The problem is that the empty lines aren’t tall enough to contain the “Delete” buttons:

To-do list short rows

Adding a minimum height will hopefully fix that:

  min-height: 40px;
Enter fullscreen mode Exit fullscreen mode

To-do list short rows fixed


OK. Next is to be able to add an entry by pressing enter instead of clicking the “Add” button and then set focus back to the input field because it’s annoying moving the cursor back and forth between them. I guess I need to listen for the “enter” key being pressed on the eventListener. The keycode for the enter key is 13:

add.addEventListener("click", function (event) {
  if (event.keyCode === 13) {
    event.preventDefault();
    let input = document.getElementById("input");
    let newEntry = document.createElement("li");
    newEntry.innerHTML =
      input.value +
      '<button id="deleteBtn" class="delete" onclick="deleteEntry(event)">Delete</button>';
    todoListID.appendChild(newEntry);
  }
});
Enter fullscreen mode Exit fullscreen mode

Doesn’t work. The user input vanishes when submitted. Must be to do with event.preventDefault(); being in the wrong place. I’ll move it up.

Nope. The input doesn’t vanish but it doesn’t get added to the to-do list. Let’s try putting the if statement on its own to check if pressing the enter key is actually working:

add.addEventListener("click", function (event) {
  event.preventDefault();
  if (event.keyCode === 13) {
    console.log("aaaaaa");
  }
  let input = document.getElementById("input");
  let newEntry = document.createElement("li");
  newEntry.innerHTML =
    input.value +
    '<button id="deleteBtn" class="delete" onclick="deleteEntry(event)">Delete</button>';
  todoListID.appendChild(newEntry);
});
Enter fullscreen mode Exit fullscreen mode

Errr…. Now everything is working as intended somehow. I don’t know how an empty if statement makes that work. I’ll take it out and see what happens. Arggghhh it still works! Which means I could have just used the enter key all along! The focus stays in the input field too. OK I’ve had enough of this again. Bye bye.

To-do list final image

Top comments (0)