Home > Software engineering >  Why does my checkbox change-handling select or deselect all rows in every table instead of just the
Why does my checkbox change-handling select or deselect all rows in every table instead of just the

Time:09-23

I have multiple tables on a page. Each table, has a "check all" checkbox in the header. In the body, there is another checkbox for each row.

When the user checks each boy row, then a active class is applied and highlights the marked row, and the counter increases/decreases.

I have a problem with the check all function.

When the user selects the check all checkbox in the header, then it should select all the rows in just that one table. I can only get it to check all the rows across all the tables. Also the counter counts all the rows across all the tables, rather than just that one table.

Where am I going wrong?

Here is my code:

// https://gomakethings.com/a-vanilla-js-foreach-helper-method/
var forEach = function forEach(arr, callback) {
  Array.prototype.forEach.call(arr, callback);
};

var tableInputs = document.querySelectorAll('.table tbody td .form-check-input');
var tableSelectAll = document.querySelectorAll('.table thead th .form-check-input');
var count = document.querySelector('.output span')

forEach(tableInputs, function(element) {
  element.addEventListener('change', function() {
    // active class to make row blue
    if (element.checked) {
      element.parentNode.parentNode.classList.add('active');
    } else {
      element.parentNode.parentNode.classList.remove('active');
    }

    // set count to -
    var numberSelected = 0;

    // count number of checked
    for (var i = 0; i < tableInputs.length; i  ) {
      if (tableInputs[i].checked == true) {
        numberSelected  ;
      }
    }

    // display the count
    count.innerHTML = numberSelected;
  });
});

forEach(tableSelectAll, function(element) {
  element.addEventListener('change', function() {

    if (element.checked == true) {
      forEach(tableInputs, function(input) {
        input.parentNode.parentNode.classList.add('active');
        input.checked = true;

        // set count to -
        var numberSelected = 0;

        // count number of checked
        for (var i = 0; i < tableInputs.length; i  ) {
          if (tableInputs[i].checked == true) {
            numberSelected  ;
          }
        }

        // display the count
        count.innerHTML = numberSelected;
      });
    } else {
      forEach(tableInputs, function(input) {
        input.parentNode.parentNode.classList.remove('active');
        input.checked = false;
        count.innerHTML = 0;
      });
    }
  });
});
.form-check-input {
  border: solid 1px #000;
  position: relative;
}

tr.active {
  background-color: lightblue;
}

body { margin: 0; zoom: .88; }
p { margin: 0; }
<div >
  <div >
    <div >
      <p>Table 1</p>

      <table >
        <thead>
          <tr>
            <th><input  type="checkbox" value=""></th>
            <th>Request date</th>
            <th>Name</th>
            <th>Organisation/Employer</th>
            <th>Selected Course(s)</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td><input  type="checkbox" value=""></td>
            <td>10/10/2014</td>
            <td><a href="#">Clark Kent</a></td>
            <td><span>Daily Planet</span></td>
            <td><span>Flight</span></td>
          </tr>
          <tr>
            <td><input  type="checkbox" value=""></td>
            <td>10/10/2014</td>
            <td><a href="#">Hal Jordan</a></td>
            <td><span>Green Lantern Corps</span></td>
            <td>Lighting</td>
          </tr>
          <tr>
            <td><input  type="checkbox" value=""></td>
            <td>10/10/2014</td>
            <td><a href="#">Arthur Curry</a></td>
            <td><span>Atlantis Water</span></td>
            <td>Aquatics</td>
          </tr>
        </tbody>
      </table>

      <p>Table 2</p>
      <table >
        <thead>
          <tr>
            <th><input  type="checkbox" value=""></th>
            <th>Request date</th>
            <th>Name</th>
            <th>Organisation/Employer</th>
            <th>Selected Course(s)</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td><input  type="checkbox" value=""></td>
            <td>10/10/2014</td>
            <td><a href="#">Barry Allen</a></td>
            <td><span>Star Labs</span></td>
            <td><span>Speed</span></td>
          </tr>
          <tr>
            <td><input  type="checkbox" value=""></td>
            <td>10/10/2014</td>
            <td><a href="#">Bruce Wayne</a></td>
            <td><span>Wayne Enterprises</span></td>
            <td>Combat</td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</div>

<p >Total selected: <span>0</span></p>

CodePudding user response:

Regardless of the approach one always should break down the problem into specific tasks which for the OP's requirements are ...

  • initialize a checkbox related change handling.

  • on any checkbox' state change do update all checkbox depended states.

  • do update the checkbox counter at init time and at checkbox state change time.

  • for any (initially) checked checkbox update its related table row state as well.

The techniques/tools are Event Delegation and the Selectors API

At any checkbox state change the handler inspects the event target whether it belongs to the current table's header or body.

Based on this check one either, for the first case, needs to check/uncheck all of a current table's body-related checkboxes or, according to the second case, one needs to update the state of the sole header-related checkbox.

Updating the checkbox counter is achieved by the correct selector and the queried node list's length value.

function updateCheckboxCounter() {
  document
    .querySelector('.output > span')
    .textContent = document
      .querySelectorAll('table.table tbody [type="checkbox"]:checked')
      .length;
}

function updateTableRowActiveState(checkboxNode) {
  checkboxNode
    .closest('tr')
    .classList
    .toggle('active', checkboxNode.checked);
}

function updateCheckboxDependedStates({ target }) {
  const tableNode = target.closest('table.table');

  if (target.matches('thead [type="checkbox"]')) {

    tableNode
      .querySelectorAll('tbody [type="checkbox"]')
      .forEach(elmNode => {

        elmNode.checked = target.checked;

        updateTableRowActiveState(elmNode);
      });

  } else if (target.matches('tbody [type="checkbox"]')) {

    tableNode
      .querySelector('thead [type="checkbox"]')
      .checked = Array
        .from(
          target
            .closest('tbody')
            .querySelectorAll('[type="checkbox"]')
        )
        .every(elmNode => elmNode.checked);

    updateTableRowActiveState(target);
  }
  updateCheckboxCounter();
}

function init() {
  document
    .querySelectorAll('table.table')
    .forEach(elmNode =>

      elmNode.addEventListener('change', updateCheckboxDependedStates)
    );
  document
    .querySelectorAll('table.table tbody [type="checkbox"]:checked')
    .forEach(updateTableRowActiveState);

  updateCheckboxCounter();
}
init();
.form-check-input {
  border: solid 1px #000;
  position: relative;
}
tr.active {
  background-color: lightblue;
}

body { margin: 0; zoom: .88; }
p { margin: 0; }
<div >
  <div >
    <div >

      <p>Table 1</p>

      <table >
        <thead>
          <tr>
            <th><input  type="checkbox" value=""></th>
            <th>Request date</th>
            <th>Name</th>
            <th>Organisation/Employer</th>
            <th>Selected Course(s)</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td><input  type="checkbox" value=""></td>
            <td>10/10/2014</td>
            <td><a href="#">Clark Kent</a></td>
            <td><span>Daily Planet</span></td>
            <td><span>Flight</span></td>
          </tr>
          <tr>
            <td><input  type="checkbox" value=""></td>
            <td>10/10/2014</td>
            <td><a href="#">Hal Jordan</a></td>
            <td><span>Green Lantern Corps</span></td>
            <td>Lighting</td>
          </tr>
          <tr>
            <td><input  type="checkbox" value="" checked></td>
            <td>10/10/2014</td>
            <td><a href="#">Arthur Curry</a></td>
            <td><span>Atlantis Water</span></td>
            <td>Aquatics</td>
          </tr>
        </tbody>
      </table>

      <p>Table 2</p>

      <table >
        <thead>
          <tr>
            <th><input  type="checkbox" value=""></th>
            <th>Request date</th>
            <th>Name</th>
            <th>Organisation/Employer</th>
            <th>Selected Course(s)</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td><input  type="checkbox" value="" checked></td>
            <td>10/10/2014</td>
            <td><a href="#">Barry Allen</a></td>
            <td><span>Star Labs</span></td>
            <td><span>Speed</span></td>
          </tr>
          <tr>
            <td><input  type="checkbox" value=""></td>
            <td>10/10/2014</td>
            <td><a href="#">Bruce Wayne</a></td>
            <td><span>Wayne Enterprises</span></td>
            <td>Combat</td>
          </tr>
        </tbody>
      </table>

    </div>
  </div>
</div>

<p >Total selected: <span>0</span></p>

  • Related