Welcome to another interactive session in our course, "Making a Dynamic Todo List with JavaScript." Today, we are stepping into one of the most critical parts of any todo list — checking off items. This lesson will guide you through how to add checkboxes, make them interactive, and solve some problems that arise with this change.
Our journey will include topics like checkboxes, handling change events, manipulating CSS properties like text decorations in JavaScript, and implementing a delete button. With these functionalities, our todo list will be that much closer to being a fully interactive application.
These skills are essential not just for todo lists but also for setting preferences in forms or managing project tasks in a collaborative tool. Ready for the journey? Let's roll!
Let's begin our journey by examining the concept of checkboxes in HTML and JavaScript. In web development, checkboxes are commonly used for selection purposes. As you may have seen in online forms or surveys, checkboxes permit multiple selections from a group of options.
Just like text fields, checkboxes are a way to receive input from the user. In our todo list, we use checkboxes to signify whether a task is completed. Let's consider the following HTML
snippet to create a new checkbox:
HTML, XML1<input type="checkbox">
This is useful when you are adding static items to the webpage, but in our case, where we want to dynamically add new checkboxes to any tasks that may be added to the todo list, it makes more sense to use JavaScript instead:
JavaScript1const checkbox = document.createElement('input'); 2checkbox.type = 'checkbox'; 3li.appendChild(checkbox);
Here, we are using document.createElement
to create an input
element of type checkbox
, and then appending it as a child to our list item (li
). Checkboxes add interactivity to our list, allowing us to check off tasks once they're completed. Think of it like checking off items on a grocery list or marking attendance in a class.
The variable is defined using the const
keyword, indicating that its reference cannot be reassigned after initial declaration. This reduces the risk of bugs caused by accidental reassignment, making the code more predictable and easier to debug. In our todo list, using const
ensures the reference to elements like checkboxes remains constant while still allowing their properties to change.
Now that we have created a checkbox, we will want to add interactivity to it. To accomplish this, let's dive into the world of JavaScript events. In JavaScript, an event is typically an action triggered by users interacting with a webpage.
Examples of events include clicking on a button, hovering over an image, pressing a key, or, as it applies in our case, toggling a checkbox. JavaScript provides us with a way to react to these events through the addEventListener()
method. Here's how we add a "change" event listener to our checkbox:
JavaScript1checkbox.addEventListener('change', function() { 2 // Code to be run on activity 3});
In this piece of code, we assign an event listener of the type change
to our checkbox. The function passed as the second argument is then executed whenever the state of the checkbox changes. This way, our application responds in real-time to user interactions.
More on this function shortly! However, we are not only interested in when a change occurs but also what the change was, in order to handle it appropriately. This can be verified with the checked
property. For instance, we might update the appearance of the task based on whether it is marked as complete or incomplete.
JavaScript1checkbox.addEventListener('change', function() { 2 if (checkbox.checked) { 3 // The checkbox is checked 4 // Further actions can be performed here 5 } else { 6 // The checkbox is unchecked 7 // Further actions can be performed here 8 } 9});
Let's make our todo list a bit more eye-catching! Completing a task usually gives one a sense of satisfaction, right? Let's translate this into our list by striking a line through any task that has been marked as complete.
This is achieved using the textDecoration
CSS property. From our change
event function, whenever the checkbox is toggled on, we strike a line through the text. When toggled off, the line disappears.
JavaScript1checkbox.addEventListener('change', function() { 2 if (checkbox.checked) { 3 li.style.textDecoration = 'line-through'; 4 } else { 5 li.style.textDecoration = 'none'; 6 } 7});
It's just like crossing out items on a physical list, isn't it? Adding a visual indicator like this can make the interface more intuitive and satisfying for users.
Finally, while it may seem that we are done, we have introduced a bug. Can you guess what it is? Here is our full code to delete a list item as a refresher:
JavaScript1// Existing code to delete a list item 2li.addEventListener('click', function() { 3 document.getElementById('todo-list').removeChild(li); 4}); 5 6// Creating the checkbox and adding it as a child to the list item 7const checkbox = document.createElement('input'); 8checkbox.type = 'checkbox'; 9li.appendChild(checkbox); 10 11// Adding the event listener for the checkbox change 12checkbox.addEventListener('change', function() { 13 if (checkbox.checked) { 14 li.style.textDecoration = 'line-through'; 15 } else { 16 li.style.textDecoration = 'none'; 17 } 18});
The problem lies in the event listener. In JavaScript, by a process called event bubbling, all elements in the chain execute their respective event listeners. For example, if you have a li
containing a checkbox
, clicking the checkbox will trigger its click event as well as the click event of the li
. This means that even if there is a specific checkbox.addEventListener
, the li.addEventListener
will still be run. This can result in unwanted behavior, such as the entire list item being deleted when clicking the checkbox.
To avoid this, we can add a third child component that specifically takes care of the deletion process:
JavaScript1const deleteBtn = document.createElement('button'); 2deleteBtn.textContent = 'Delete'; 3deleteBtn.addEventListener('click', function() { 4 document.getElementById('todo-list').removeChild(li); 5}); 6li.appendChild(deleteBtn);
In this snippet, we create a button and label it "Delete." Then, an event listener is assigned to this button that removes the associated task from its list when clicked. This ensures that clicking the checkbox does not inadvertently delete the task because the deletion process is now handled by the delete button, not the list item itself. This separation of concerns prevents accidental deletions through event bubbling.
With the code snippet so far, we still haven't added the actual text of our todo item. We'll use the document.createTextNode
method for this purpose. This method creates a text node containing the text passed as an argument. Here's how we include this in our implementation:
JavaScript1li.appendChild(document.createTextNode(newTodo));
This line of code creates a new text node with the value of newTodo
and appends it to the li
element. We need to use createTextNode
instead of directly setting the text content of li
(e.g., li.textContent = newTodo
) because li
contains other child elements like the checkbox and delete button. WithcreateTextNode
, we can add a new text node as a child, preserving the existing structure.
To summarize everything, here is the complete implementation for adding a todo item with a checkbox to mark completion and a delete button to remove the item from the list:
JavaScript1document.getElementById('add-btn').addEventListener('click', function() { 2 const newTodo = document.getElementById('new-todo').value; 3 if (newTodo.trim() === '') return; 4 5 const li = document.createElement('li'); 6 const checkbox = document.createElement('input'); 7 checkbox.type = 'checkbox'; 8 checkbox.addEventListener('change', function() { 9 if (checkbox.checked) { 10 li.style.textDecoration = 'line-through'; 11 } else { 12 li.style.textDecoration = 'none'; 13 } 14 }); 15 16 const deleteBtn = document.createElement('button'); 17 deleteBtn.textContent = 'Delete'; 18 deleteBtn.addEventListener('click', function() { 19 document.getElementById('todo-list').removeChild(li); 20 }); 21 22 li.appendChild(checkbox); 23 li.appendChild(document.createTextNode(newTodo)); 24 li.appendChild(deleteBtn); 25 26 document.getElementById('todo-list').appendChild(li); 27 document.getElementById('new-todo').value = ''; 28});
That wraps up our lesson on Adding Check Boxes! Today, we've learned how to create and use checkboxes, incorporate dynamic text decorations, and implement a delete button in a todo list application. This knowledge strengthens our command of JavaScript and brings us closer to building fully dynamic and interactive web applications.
These functionalities are integral to our todo list. By checking or unchecking items, crossing them out, and deleting them, we can interact with and update our list to stay on top of our tasks, just as we might with a physical list.
In our next lesson, we will delve into saving and loading todo information to and from our local storage. Remember, practice makes perfect, so be sure to put your newfound knowledge into action through hands-on exercises. Happy Coding!