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 ="text" id="input" name="entry" />
<button type="submit" id="add">Add</button>
</form>
<ul id="todoListID" class="todoList"></ul>
</div>
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.
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)
});
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:
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");
});
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);
});
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");
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);
});
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");
}
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");
}
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);
}
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>';
Wahey! Success.
<button id="deleteBtn" onclick="deleteEntry(event)">
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)">
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>';
});
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();
}
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);
});
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:
const toDoListID2 = document.getElementById("toDoListID2");
todoListID2.addEventListener("click,", function (event) {
Nope
todoListID2.children.addEventListener("click,", function (event) {
Nothing
todoListID2.children[1].addEventListener("click,", function (event) {
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
);
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) {
The ID in the HTML is:
<ul id="todoListID2" class="todoList2">
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);
< ul id="todoListID2" class="todoList2">
var list = document.querySelector("ul");
console.log(list);
< 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
);
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");
}
});
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
);
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
);
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;
}
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;
}
But now the background colour doesn’t change when I click on an even item, only the text does:
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;
}
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:
Adding a minimum height will hopefully fix that:
min-height: 40px;
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);
}
});
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);
});
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.
Top comments (0)