Search code examples
javascripthtmlarraysmouseeventhtmlcollection

Array length won't increment after being emptied and can't add a mouse event on an HTMLCollection


I want to make my own grocery/task list in JavaScript and HTML. I can add things to the list no problem, but I have two problems:

  1. If I added items to the list, then press a "clear" button to erase everything in the list, it works. However, when I add things back into the list after clearing it, my console notifies me that my array is still empty, despite new tasks being shown on the screen.
  2. My second problem is bigger. See, all my tasks are in what I thought was an array, but are actually an HTML collection. So, when I try to set an onclick event, it just won't run. I have no idea why and I tried using the following question: Removing HTMLCollection elements from the DOM . It didn't work. I tried using item, HTMLCollection item() Method. Didn't work. My final attempt was using for(let thing of... but still no results.

Here is my code:

let listGro = document.getElementById("list");

let aButton = document.getElementById("add");
let cButton = document.getElementById("clear");
let tasks = document.getElementsByTagName("li");


aButton.onclick = function addItem() {
    let newThing = prompt("What do you want to add?"); // asking what the person wants

    if (newThing) { // checking if the user actually added something
        let newItemList = document.createElement("li"); //create item list
        newItemList.className = "item";
        let newItemListText = document.createTextNode(newThing); // create text
        newItemList.appendChild(newItemListText); // append text
        listGro.appendChild(newItemList); // append new element
        console.log(`New task added\nNumber of tasks in the list: ${tasks.length}`);
    } else {
        alert("You can't add nothing");
    }

};

cButton.onclick = function clearList() {
    var conf = confirm("Are you sure you want to clear the list?");
    if (conf && tasks.length != 0) {
        for (let i = 0; i < tasks.length; i++) {
            tasks[i].style.display = "none";
        }
        tasks = [];
    }

}

for(let thing of tasks) {
    //tasks[i].onclick = function removeOrEditItem() {
        /*let demand = prompt("Do you want to edit or remove the item?\nPlease answer with 'edit' or 'remove'\nIf this is a mistake, just enter nothing.");
        if (demand === "edit") {
            let editItem = prompt("What is your new thing?");
            tasks[i].innerHTML = editItem;
        } else if (demand === "remove") {
            tasks[i].splice(i, 1);
        }
        console.log("clicked");
    };*/
    
    // The thing above was a previous attempt with for(let i; i< items.length... you can work with that or below.
    thing.onclick = function removeTask() {
        thing.style.display = "none";
        console.log("removed");
    }
}
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Website template</title>
		<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css"/>
    </head>
    <body>
        <h1>Grocery lists manager</h1>
        <h2>Welcome! here you can manage your grocery list!</h2>
        <ul id = "list"><span>List:</span></ul>
        <button id = "add">Add</button>
        <button id = "clear">Clear list</button>
    </body>
    <script src = "script.js"></script>
</html>


Solution

  • The list of tasks remains empty when you clear it, because you're overwriting an HTML collection with an empty array. You assigned document.getElementsByTagName("li"); to tasks, which is a live collection. Reassigning [] to it will give it a non-live empty array. Instead of visually hiding the tasks with tasks[i].style.display="none and manually resetting the tasks collection, you should remove each task element from the DOM and let your live collection update itself automatically.

    Removing a collection from the DOM has some pitfalls, but would typically be accomplished with something like the following:

    while(tasks.length > 0) {
        tasks[0].parentNode.removeChild(tasks[0]);
    }
    

    Your event listeners are not being added because the code that adds them only runs once, when the script is loaded. And of course at the instance when the script is loaded, there are no tasks in the list. Add the event listener to the individual newItemList when it's first added.

    Make sure that you remove the task from the DOM and don't just hide it.

     newItemList.onclick = function removeTask() {
         newItemList.parentNode.removeChild(newItemList);
         console.log("removed");
     }
    

    Your complete JavaScript might look like:

    let listGro = document.getElementById("list");
    let aButton = document.getElementById("add");
    let cButton = document.getElementById("clear");
    let tasks = document.getElementsByTagName("li");
    
    aButton.onclick = function addItem() {
        let newThing = prompt("What do you want to add?"); // asking what the person wants
    
        if (newThing) { // checking if the user actually added something
            let newItemList = document.createElement("li"); //create item list
            newItemList.className = "item";
            let newItemListText = document.createTextNode(newThing); // create text
            newItemList.appendChild(newItemListText); // append text
            newItemList.onclick = function removeTask() {
                newItemList.parentNode.removeChild(newItemList);
                console.log("removed");
            }
            listGro.appendChild(newItemList); // append new element
            console.log(`New task added\nNumber of tasks in the list: ${tasks.length}`);
        } else {
            alert("You can't add nothing");
        }
    };
    
    cButton.onclick = function clearList() {
        var conf = confirm("Are you sure you want to clear the list?");
        if (conf && tasks.length != 0) {
            while(tasks.length > 0) {
                tasks[0].parentNode.removeChild(tasks[0]);
            }
        }
    };