30 Days Of JavaScript

Day 17: Events

Lesson 1: onclick

Basic onclick handler

Set your HTML to this.

1<button id="hi-button" onclick="sayHi()">Say Hi!</button>

And in your JavaScript

1const sayHi = () => {
2  console.log("Hi");
3};

We have added an attribute to the <button> element called onclick that executes whatever code we pass in. In this case execute the sayHi function. We say that the onclick handler executes the sayHi function

Now when you click the button you will see "Hi" in the console.

It's really important to understand here that you aren't passing sayHi to the onclick handler, you are passing the line of code sayHi() which happens to be an invocation of the sayHi function.

Change onclick="sayHi()" to onclick="sayHi" and then click the button. You'll see that nothing appears in the console. This shows that the code we put in the " " isn't a reference to a function that will be executed, but instead the code that will be run.

Why am I hammering on this so much?

Because this is the number one issue I see new developers continue to struggle with, as they move further into their developer career and start to use things like React and Vue.

For now, all you need to remember is that whatever is between those " " marks is what will be executed.

If you wanted to run the same code without the function call you can do this.

1<button id="hi-button" onclick="console.log('hi')">Say Hi!</button>

Notice I had to change from double quotes to single quotes in the console.log? If I had kept the double quotes the HTML would have gotten confused thinking you were closing the onclick quotes.

Event listener for onclick

Let's go back to our todo listener

1<div id="welcome-message">Welcome</div>
2<ul id="todos"></ul>
1const todos = [
2  { description: "Walk dog", important: true },
3  { description: "Watch TV", important: false },
4  { description: "Bake cake", important: false },
5];
6
7const todosList = document.getElementById("todos");
8
9todos.forEach((todoItem) => {
10  const listItem = document.createElement("li");
11  const content = document.createTextNode(todoItem.description);
12
13  if (todoItem.important) {
14    listItem.classList.add("warning");
15  }
16
17  listItem.appendChild(content);
18  todosList.appendChild(listItem);
19});

What if we want to click a done button and have an item removed from the list?

Well we can add a button to each item to do just that.

We need to create a button, set the text that will appear on the button and then add it to the <li> so it appears in the list.

Change your forEach to look like this.

1todos.forEach((todoItem) => {
2  const listItem = document.createElement("li");
3  const content = document.createTextNode(todoItem.description);
4
5  // creates button
6  const doneButton = document.createElement("button");
7
8  //set the text with the innerHTML property
9  doneButton.innerHTML = "Done";
10
11  if (todoItem.important) {
12    listItem.classList.add("warning");
13  }
14
15  listItem.appendChild(content);
16
17  // adds the button to the list item
18  listItem.appendChild(doneButton);
19
20  todosList.appendChild(listItem);
21});

Now we have a button for each item, but if you click them they don't do anything 😞 Let's fix that.

The best way to do this is via the .onclick property that our doneButton comes with. As we saw above, it executes what ever code we give it. That means we need to give it a function that it will execute.

After we set the innerHTML add this.

1doneButton.onclick = () => {
2  console.log("hello");
3};

We create an anonymous function () => {} that has a console.log() inside it.

This is different to when we added it in the HTML. In the HTML we just wrote the contents of the function, we didn't have to create the funciton, the HTML and browser took care of that for us.

Don't believe me? Try this.

1doneButton.onclick = console.log("hello");

Doesn't work right?

Ok so now we need to write some code that will remove the <li> element from the DOM.

Amazingly it's just one line of code, but this line of code has a couple of new concepts.

Firstly, the function we assign to button.onclick that gets called when we click the button, will be passed a parameter called event. This is a large object with lots of information, what we care about is the .target property as they gives us a reference to which element actually caused this event to fire.

The second concept is that the .target property is an HTML DOM element and so it knows where it is in the tree and, importantly for us, can give us access to the <li> that our <button> is in. Once we get access to that we can ask it to delete itself.

So, change your event handler to this.

1doneButton.onclick = (event) => {
2  event.target.parentNode.remove();
3};

Now click your Done button and see that item removed from the screen.

While this removes the element from the DOM it doesn't actually remove the data from the todos array.

You can see this by adding console.log(todos); right after we .remove().

So our DOM is actually now out of sync with the data that makes it. For this example it doesn't matter very much however for completeness we should delete it from the array as well.

To do this we need to get the item description (Walk dog, Bake cake etc) and then find that item from the array and remove it.

See if you can solve that yourself 😀

Hints:

  1. You need someway to get the description text from the <li>. This line listItem.id = todoItem.description; added right after you create the li should help. Along with console.log(event.target.parentNode.id);

  2. If the id from the li matches the description in the array we can remove it. You can do this with .filter() to filter out the item you don't want.

  3. Remember to get you <li> id before you remove it from the DOM.

Outline

Go Pro?

If you upgraded to pro, sign in here

  • About
  • Blog
  • Privacy
Looking to email me? You can get me on my first name at allthecode.co