What is the best way to choose all the checkboxes in a checkbox list, such as a ToDo List?

I am currently working on developing a to-do list where each item includes a checkbox. My goal is to be able to select these checkboxes individually by clicking on them. Furthermore, I would like the checkboxes to toggle between being checked and unchecked when clicked on multiple times.

To implement this functionality, I have set up a basic structure with an input field and an add button. Upon clicking the add button, a new row is added to the table using the appendChild method. I have also created and appended all the necessary elements to ensure that the newly added row displays correctly, including the checkboxes.

However, a challenge arises when I try to select multiple checkboxes across different rows. While the functionality works flawlessly when only a single checkbox is present, it does not work as expected when there are multiple checkboxes involved.

const addButton = document.querySelector(".search_add_container button");
const table = document.querySelector("table");

addButton.addEventListener("click", function() {
  var inputValue = document.getElementById("inputContent");
  var tableRow = document.createElement("tr");
  var dataCell = document.createElement("td");
  var dataCellInnerContainer = document.createElement("div");
  dataCellInnerContainer.className = "data_cell_container";
  var tableCheckboxes = document.createElement("div");
  tableCheckboxes.className = "checkbox_box";
  var img = document.createElement("img");
  img.src = "starGrey.png";
  img.alt = "starGrey";
  var cellHeader = document.createElement("h5");
  cellHeader.className = "cell_header";
  var cellTent = document.createElement("h5");
  cellTent.className = "cell_tent";
  var time = document.createElement("p");



  if (inputValue.value == "") {
    alert("Please add a note!");
  } else {
    cellHeader.innerText = inputValue.value;
    cellTent.innerText = inputValue.value;
    time.innerText = new Date().toLocaleTimeString([], {
      hour: '2-digit',
      minute: "2-digit"
    });
    table.appendChild(tableRow);
    tableRow.appendChild(dataCell);
    dataCell.appendChild(dataCellInnerContainer);
    dataCellInnerContainer.appendChild(tableCheckboxes);
    dataCellInnerContainer.appendChild(img);
    dataCellInnerContainer.appendChild(cellHeader);
    dataCellInnerContainer.appendChild(cellTent);
    dataCellInnerContainer.appendChild(time);

    inputValue.value = "";

  }

  var checkboxes = document.querySelectorAll(".data_cell_container .checkbox_box");
  checkboxes.forEach(checkbox => {
    checkbox.addEventListener("click", () => {
      if (checkbox.classList.contains("checked")) {
        checkbox.classList.remove("checked");
      } else {
        checkbox.classList.add("checked");
      }
    });
  });

  inputValue.value = "";
});
table {
  width: 100%;
}

tr td {
  position: relative;
  display: table-cell;
  vertical-align: center;
  border: 1px solid black;
}

td.checked {
  text-decoration: line-through;
}

.data_cell_container {
  position: relative;
  display: grid;
  grid-template-columns: 30px 30px calc(250px - 80px) auto 65px;
  grid-gap: 5px;
  padding: 5px 0 5px 20px;
}

.data_cell_container {
  position: relative;
  padding: 8px 20px;
}

.checkbox_box {
  position: relative;
  width: 16px;
  height: 16px;
  border: 2px solid #BABCBE;
  background-color: transparent;
  cursor: pointer;
}

.checkbox_box.checked::after {
  content: '';
  position: absolute;
  left: 3px;
  height: 4px;
  width: 10px;
  border-left: 2px solid #BABCBE;
  border-bottom: 2px solid #BABCBE;
  transform: rotate(-45deg);
  transform-origin: bottom;
}

.cell_header {
  max-width: calc(250px - 100px);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.cell_tent {
  max-width: auto;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.data_cell_container p {
  font-size: smaller;
  font-weight: bolder;
}
<header>
  <div class="search_add_container">
    <input type="text" placeholder="Add New List Item" id="inputContent">
    <img src="search.png" alt="searchicon">
    <button>Add</button>
  </div>
</header>

<div class="table_container">
  <table>

  </table>
</div>

Answer №1

With each click of the add button (under the right circumstances, of course), an additional event listener is assigned to each previous checkbox. Initially, only one event listener is attached to the checkbox.

Upon the second click, the first checkbox now has two duplicate event listeners for the click event. Consequently, checking the checkbox will immediately trigger an unchecked state (if it was previously checked).

On the third click, the first checkbox accumulates three identical event listeners. Thus, upon clicking, the checkbox will toggle between being checked and unchecked multiple times due to the multiple event listeners.

To prevent the buildup of event listeners, consider following this suggestion:

const addButton = document.querySelector(".search_add_container button");
const table = document.querySelector("table");
addButton.addEventListener("click", function(){
    var inputValue = document.getElementById("inputContent");
    var tableRow = document.createElement("tr");
    var dataCell = document.createElement("td");
    var dataCellInnerContainer = document.createElement("div");
    dataCellInnerContainer.className = "data_cell_container";
    var tableCheckboxes = document.createElement("div");
    tableCheckboxes.className = "checkbox_box";
    var img = document.createElement("img");
    img.src = "starGrey.png";
    img.alt = "starGrey";
    var cellHeader = document.createElement("h5");
    cellHeader.className = "cell_header";
    var cellTent = document.createElement("h5");
    cellTent.className = "cell_tent";
    var time = document.createElement("p");

    if(inputValue.value === ""){
        alert("Please add a note!");
    }else{
        cellHeader.innerText = inputValue.value;
        cellTent.innerText = inputValue.value;
        time.innerText = new Date().toLocaleTimeString([], { hour: '2-digit', minute: "2-digit" });
        table.appendChild(tableRow);
        tableRow.appendChild(dataCell);
        dataCell.appendChild(dataCellInnerContainer);
        dataCellInnerContainer.appendChild(tableCheckboxes);
        dataCellInnerContainer.appendChild(img);
        dataCellInnerContainer.appendChild(cellHeader);
        dataCellInnerContainer.appendChild(cellTent);
        dataCellInnerContainer.appendChild(time);

        inputValue.value = "";

    }
    
    var checkboxes = document.querySelectorAll(".data_cell_container .checkbox_box");
    let checkBoxIndex = 0;
    checkboxes.forEach(checkbox => {
      if(checkBoxIndex === (checkboxes.length) - 1){
        checkbox.addEventListener("click", () => {
            if(checkbox.classList.contains("checked")){
                checkbox.classList.remove("checked");
            }
            else{
                checkbox.classList.add("checked");
            }
        });
      }
      checkBoxIndex++;
    });
  
    inputValue.value = "";
});

The decision to use a new variable instead of indexOf() was made to ensure the correct value is retrieved when dealing with multiple similar entities in an array. The same principle applies to lastIndexOf().

We hope you find this solution beneficial.

Answer №2

When dealing with multiple click handlers on a single checkbox, the best approach is to simplify your code like this -

const addButton = document.querySelector(".search_add_container button");
const table = document.querySelector("table");

addButton.addEventListener("click", function() {
  var inputValue = document.getElementById("inputContent");
  var tableRow = document.createElement("tr");
  var dataCell = document.createElement("td");
  var dataCellInnerContainer = document.createElement("div");
  dataCellInnerContainer.className = "data_cell_container";
  var tableCheckboxes = document.createElement("div");
  tableCheckboxes.className = "checkbox_box";
  var img = document.createElement("img");
  img.src = "starGrey.png";
  img.alt = "starGrey";
  var cellHeader = document.createElement("h5");
  cellHeader.className = "cell_header";
  var cellTent = document.createElement("h5");
  cellTent.className = "cell_tent";
  var time = document.createElement("p");



  if (inputValue.value == "") {
    alert("Please add a note!");
  } else {
    cellHeader.innerText = inputValue.value;
    cellTent.innerText = inputValue.value;
    time.innerText = new Date().toLocaleTimeString([], {
      hour: '2-digit',
      minute: "2-digit"
    });
    table.appendChild(tableRow);
    tableRow.appendChild(dataCell);
    dataCell.appendChild(dataCellInnerContainer);
    dataCellInnerContainer.appendChild(tableCheckboxes);
    dataCellInnerContainer.appendChild(img);
    dataCellInnerContainer.appendChild(cellHeader);
    dataCellInnerContainer.appendChild(cellTent);
    dataCellInnerContainer.appendChild(time);

    inputValue.value = "";

  }

  var checkboxes = document.querySelectorAll(".data_cell_container .checkbox_box");
  var checkbox = checkboxes[checkboxes.length - 1]
    checkbox.addEventListener("click", () => {
      if (checkbox.classList.contains("checked")) {
        checkbox.classList.remove("checked");
      } else {
        checkbox.classList.add("checked");
      }
    });

  inputValue.value = "";
});
table {
  width: 100%;
}

tr td {
  position: relative;
  display: table-cell;
  vertical-align: center;
  border: 1px solid black;
}

td.checked {
  text-decoration: line-through;
}

.data_cell_container {
  position: relative;
  display: grid;
  grid-template-columns: 30px 30px calc(250px - 80px) auto 65px;
  grid-gap: 5px;
  padding: 5px 0 5px 20px;
}

.data_cell_container {
  position: relative;
  padding: 8px 20px;
}

.checkbox_box {
  position: relative;
  width: 16px;
  height: 16px;
  border: 2px solid #BABCBE;
  background-color: transparent;
  cursor: pointer;
}

.checkbox_box.checked::after {
  content: '';
  position: absolute;
  left: 3px;
  height: 4px;
  width: 10px;
  border-left: 2px solid #BABCBE;
  border-bottom: 2px solid #BABCBE;
  transform: rotate(-45deg);
  transform-origin: bottom;
}

.cell_header {
  max-width: calc(250px - 100px);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.cell_tent {
  max-width: auto;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.data_cell_container p {
  font-size: smaller;
  font-weight: bolder;
}
<header>
  <div class="search_add_container">
    <input type="text" placeholder="Add New List Item" id="inputContent">
    <img src="search.png" alt="searchicon">
    <button>Add</button>
  </div>
</header>

<div class="table_container">
  <table>

  </table>
</div>

Ensure that only the most recent element is targeted for selection.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The number of contents determines the dynamic CSS border style

Could you please assist me in identifying what this design element is called? I have tried searching on Google, but had no luck finding exactly what I am looking for. Here is an example of the look I am aiming for: https://i.sstatic.net/AXUvq.jpg Can so ...

The Dialog feature offered by jQuery-UI

Having just started with jQuery, I am looking to implement the jQuery-UI Dialog to display a message containing lengthy text to the user. My goal is to have a "More details" link in each row of my HTML table that will trigger the jQuery Dialog window to op ...

Having trouble with displaying the CSS background image?

I've been attempting to configure background images using CSS, but for some reason, I'm not able to get the images to show up correctly. Below is the CSS code that I'm working with: a.fb { background-image: url('img/Facebook.png&a ...

How to pass the Node environment to layout.jade in Express without explicitly specifying the route

Passing parameters to Jade files seems like a piece of cake: app.use('/myroute', function (req, res) { res.render('myview', {somevar: 'Testing!'}); }); But, I have my layout.jade file that is automatically read and rendere ...

Closing the dropdown menu by directly clicking on the button

I've noticed several inquiries on this platform regarding how to close a drop-down menu by clicking anywhere outside of it. However, my question is a bit different. I want the dropdown-menu to remain open once clicked, only closing when the user clic ...

developing a PHP script that dynamically generates active classes

I am working on building a dynamic menu that is based on the categories stored in the database. I need the selected menu option to have the css class 'active', indicating which page the user is currently on. The pages themselves are also generate ...

Click on the div to automatically insert its text into the textarea

I am looking for a way to enable users to edit their posts easily. My idea is to have them click on a link, which will then hide the original div containing their post and reveal a new div with the old text inside a textarea for editing. Despite searching ...

The verification of form is not done using an if statement

There are two forms in my code named formA and comments that I need to submit via AJAX. However, the current if and else conditions do not correctly identify the form and always trigger the alert message hello3. Here is the JavaScript function: function ...

Sort various divs using a list

I have multiple divs containing different content. On the left side, there is a list of various categories. When a category is clicked, I want to display the corresponding div for that category. Initially, I want the main category to be loaded, with no opt ...

The Navbar is throwing a TypeError because it is unable to retrieve the property 'classList' of null

I am currently experimenting with building a navbar in HTML that has the capability to dynamically switch pages without changing links using href. The method I'm employing to achieve this involves adding a classList of is-active to the div elements wi ...

Updating data from an API within a div using AJAX calls in a React application

I have designed a React template to showcase live football scores in the following manner: const LiveScore = () => { const {direction} = useThemeProvider(); const [selectedDay, setSelectedDay] = useState(parseInt(dayjs().format('DD'))); retur ...

Refresh TR when clicked

I have a HTML table that lists items from SQL, using <tr> and <td>. The table is housed within a div that is refreshed every 30 seconds with jQuery AJAX (hence the unique id on the div). Here is the HTML code: function auto_load() { $.aj ...

Issue: Unable to access GET request with Express and handlebars

Hello everyone, I'm just getting started with JS/Handlebars and I'm facing an issue trying to display an image from my home.hbs file in VS Code. When I start the server, this is the message I receive: https://i.sstatic.net/wUxB7.jpg Below is th ...

Error: The variable success_msg has not been defined in the EJS Reference

I am in the process of developing a library website for my school that includes login and administration capabilities. I am relatively new to node.js and EJS, but I recently revamped the routing and page delivery system to use EJS and express. As part of u ...

Can fog be applied from a fixed location within a three.js scene without being tied to the camera's position?

Is it feasible to render fog in such a way that an avatar on a terrain remains clear while the surrounding area gradually fades into the mist, especially when the camera is positioned overhead at a distance? ...

Click event for jQuery horizontal accordion

I'm attempting to create a simple horizontal accordion-style element. My setup includes three 'banner' divs and three 'area' divs. Ideally, when I click on a banner, the corresponding area should animate - expanding in width to au ...

Internet Explorer fails to accurately determine the height of a textarea based on the line-height

I need to adjust the display of a textarea to show 4 rows with a line height set to 200%. Take a look at the HTML code: <textarea rows="4" cols="50"> 1 2 3 4</textarea> and the accompanying CSS: textarea { line-height: 2; } It appears ...

Is Fetch executed before or after setState is executed?

I've encountered an issue while trying to send data from the frontend (using React) to the backend (Express) via an HTML form, and subsequently clearing the fields after submission. The code snippet below illustrates what I'm facing. In this scen ...

What could be causing the issue with retrieving HTTP requests in Nest.js even after configuring the controller?

After the individual who departed from the company utilized Nest.js to create this server-side system. They established the auth.controller, auth.service, auth.module, auth-token, jwt.strategy, and jwt-payload, with everything properly configured. I verifi ...

Tips for closing print window dialog during Protractor testing

Currently, I am performing end-to-end testing using protractor. During a specific test, I need to verify if the print button is successfully creating a PDF. When the test clicks on the button, it triggers a print window dialog as shown below: https://i.st ...