Search code examples
javascripthtmlarraysclickaddeventlistener

JavaScript - counting clicks on array values while presenting them in a nested loop


I am trying to extend some sample code I found in this LogRocket blog article. The code is for a to-do app using localStorage. I want to extend it in the following way:

First, let me explain what I do on paper when I'm trying to decide between choices. Say I have a list of 5 options, maybe places to volunteer for work. I look at the first and second choices and I put a checkmark by the one I prefer. I then compare the first and third choices and put a checkmark by the one I prefer. I then do the first and fourth and first and fifth. Then, I move down to the second choice and compare the second and third, then the second and fourth, then the second and fifth. Then it's the third and fourth and third and fifth, then finally the fourth and fifth. When I'm done I have compared all choices against each other, and the items are ranked from 4 checkmarks down to 0 checkmarks.

This may sound goofy to you, but it works for me. And I want to be able to do it as an extension of the LogRocket code. Regardless of whether it's the best way to prioritize, I just want to know how to implement this particular way of coding.

So below the list of tasks populated by the user is a Prioritize button. Below that I have two empty divs with plus icons next to them. In the code I have an empty Counter array. When the user clicks the Prioritize button the code creates the same number of values in the Counter array as there are to do tasks. Each of those values are set to 0. Next I have a for loop with another for loop nested in that. The for loops are for displaying the tasks to be compared. What should happen when the button is clicked is that the screen displays in the divs the first and second tasks. When the user clicks either of the plus icons next to one of the divs, the value in the counter array at either the first or second index is incremented by 1. Then the second task disappears from the second div and the third task appears, and upon a click the value at either the first or third index of the Counter array is incremented, etc. Once the inner for loop has displayed all other tasks besides the first in the second div, the first task disappears from the first div and the second task appears there, with the remaining tasks looping through in the second div. You can see where it should go from there.

In my current code I have event listeners in the for loops to try to capture the clicks, but the for loop is not waiting for those clicks. Instead, the for loops run completely through so that on the screen I am only seeing the second to last task in the first div and the last task in the second div. I also have console logs to show the number of clicks. To test this out I put in 5 tasks. The click event is currently on the divs themselves. When the screen displayed just the 4th and 5th tasks and I clicked on the 4th task, the code said items 1 through 4 (indexes 0-3) were now 1, and when I clicked the 5th task, it said items 1 through 4 were equal to 2. So instead of incrementing the value at the index of the task that was clicked, it incremented the first four values simultaneously, plus when the fifth value was clicked, it incremented the others but not the fifth itself.

At first I thought the issue might be one of how to pause a for loop until a click occurs, but now I think my whole approach may be wrong. Here is the code at issue:

The HTML (from within the body element):

<!-- LogRocket code -->
<div class="container">
    <div class="to-do-app">
        <h2>To-do App</h2>
        <br>
        <input type="text" id="item" placeholder="Enter item...">
        <br><br>
        <button onclick="add()">Add Item <i class="fa-solid fa-plus"></i></button>
        <button onclick="del()">Clear all <i class="fa-solid fa-ban"></i></button>
    </div>
    <ul class="to-do-list"></ul>
<!-- end of LogRocket code -->
<!-- my code -->
    <button onclick="prioritize()">Prioritize</button>
    <div id="task1"><i class="fa-solid fa-circle-plus"></i></div>
    <div id="task2"><i class="fa-solid fa-circle-plus"></i></div>
</div>
<script src="./script.js"></script>

The CSS:

/* all LogRocket code */
@import url("https://fonts.googleapis.com/css2?family=Asap&display=swap");
* {
    padding: 0;
    margin: 0;
    box-sizing: border-box;
}
body {
    width: 100%;
    height: 100vh;
    background-color: #e0d6e9;
    font-family: "Asap", sans-serif;
}
.container {
    max-width: 405px;
    margin: 137px auto;
    padding: 20px;
    display: flex;
    flex-direction: column;
}
.to-do-app {
    width: 100%;
    padding: 20px;
    border-radius: 5px;
    background-color: whitesmoke;
    border: 1px solid #d3d3d3;
}
.to-do-app h2 {
    padding: 10px;
}
.to-do-app input {
    width: 250px;
    padding: 5px;
    border-radius: 5px;
    border: 1px solid #d3d3d3;
}
.to-do-app button {
    width: fit-content;
    padding: 5px;
    cursor: pointer;
    border: 1px solid #d3d3d3;
    border-radius: 5px;
    background-color: whitesmoke;
}
.to-do-app button:hover {
    background-color: rgba(0, 0, 0, 0.1);
}
li {
    font-size: 1.5rem;
}
.to-do-list {
    margin-top: 20px;
    margin-right: 5px;
    padding: 0 20px 10px 25px;
    display: flex;
    flex-direction: column;
    gap: 15px;
    list-style: none;
}
.to-do-list li {
    font-size: small;
    background-color: whitesmoke;
    padding: 20px;
}

JavaScript:

// mostly LogRocket code except for counter declaration
const ul = document.querySelector('ul');
const input = document.getElementById('item');
let itemsArray = localStorage.getItem('items') ? JSON.parse(localStorage.getItem('items')) : [];
let counter = [];

itemsArray.forEach(addTask);
function addTask(text) {
    const li = document.createElement('li');
    li.textContent = text;
    ul.appendChild(li);

}
function add() {
    itemsArray.push(input.value);
    localStorage.setItem('items', JSON.stringify(itemsArray));
    addTask(input.value);
    input.value = '';
}

function del() {
    localStorage.clear();
    ul.innerHTML = '';
    itemsArray = [];
}
// end of LogRocket code and beginning of my code
function prioritize() {
    let task1 = document.getElementById("task1");
    let task2 = document.getElementById("task2");
    let task1span = task1.appendChild(document.createElement("span"));
    let task2span = task2.appendChild(document.createElement("span"));
    for (let i = 0; i < itemsArray.length; i++) {
        counter[i] = 0;
        console.log('Item ' + (i + 1) + ' = ' + counter[i]);
    }
    for (let j = 0; j < itemsArray.length-1; j++) {
        task1span.textContent = ``;
        task1span.textContent = `${itemsArray[j]}`;
        task1span.addEventListener("click", function () {
            counter[j] += 1; 
            console.log('Item ' + (j + 1) + ' = ' + counter[j]);
        });

        for (let k = 0; k < itemsArray.length; k++) {
            task2.textContent = ``;
            task2.textContent = `${itemsArray[k]}`;
            task2span.addEventListener("click", function () {
                counter[k] += 1;
                console.log('Item ' + (k + 1) + ' = ' + counter[k]);
            });
        }
    }
    console.log('Final tally');
    for (let l = 0; l < itemsArray.length; l++) {
        console.log(`Item ${l + 1} = ${counter[l]}`);
    }
}

Any help is appreciated.


Solution

  • Do you want to compare all pairs, letting the user pick the winner. In the end you want to sort it by number of wins?

    Here's a simplified way of doing that.

    const list = ["Task 1", "Task 2", "Task 3", "Task 4"];
    const taskContainer = document.getElementById("taskContainer");
    const counter = new Array(list.length).fill(0);
    
    function showAllPairs() {
      taskContainer.innerHTML = "";
    
      for (let i = 0; i < list.length - 1; i++) {
        for (let j = i + 1; j < list.length; j++) {
          taskContainer.innerHTML += `
            <div class="pair active">
              <div class="item">
                <p><button onclick="incrementCounter(this, ${i})">+</button> ${list[i]}</p>
              </div>
              <div class="item">
                <p><button onclick="incrementCounter(this, ${j})">+</button> ${list[j]}</p>
              </div>
            </div>
          `;
        }
      }
    }
    
    function incrementCounter(elem, index) {
      counter[index]++;
      elem.closest(".pair").classList.remove("active");
      console.log("" + list + " = " + counter)
    }
    
    showAllPairs();
    .pair {
      display: none;
    }
    
    .pair.active {
      display: flex;
    }
    
    .item {
      padding: 5px 20px;
    }
    Let's priortize 4 tasks
    <div id="taskContainer"></div>