Home > Back-end >  How to add a background color to the td element when you check the checkbox in it
How to add a background color to the td element when you check the checkbox in it

Time:09-28

I am trying to iterate through the checkboxes and give a green background color to the checkboxes which were ticked.

I have the main checkbox which disables the rest of the checkboxes so the disabled checkboxes should not have a green background color as they are disabled.

Also I will have more rows in the table and I wonder how am I suppose to avoid repeat the same block of code on every row with tons of new classes.

let carParkCheckMain = document.querySelector('.car-park-check-main');
let carParkChecks = document.querySelectorAll('.car-park-check');
let tdAll = document.querySelectorAll('.access-td');

// Uncheck all checkboxes
for (let i = 0; i < carParkChecks.length; i  ) {
  disable(carParkChecks[i]);
}

// Run through
for (let td of tdAll) {
  td.addEventListener('mouseenter', () => {
    td.addEventListener('click', () => {
      if (carParkCheckMain.checked) {
        for (let i = 0; i < carParkChecks.length; i  ) {
          enable(carParkChecks[i]);
        }
      } else {
        for (let i = 0; i < carParkChecks.length; i  ) {
          disable(carParkChecks[i]);
        }
      }
    })
  })
}


function disable(checkbox) {
  return checkbox.disabled = true;
}

function enable(checkbox) {
  return checkbox.disabled = false;
}

function greenBg(element) {
  element.classList.toggle('td-active');
}
.td-active {
  background-color: rgb(95, 241, 95) !important;
}
<table class="table table-bordered access-table">
  <tbody>
    <tr>
      <th scope="row" style="font-weight: 300;">Checkboxes</th>
      <td class="main-td access-td one">
        <label>
                                        <input type="checkbox" class="access-table-input car-park-check-main"> main checkbox
                                    </label>
      </td>
      <td class="base-td access-td two">
        <label>
                                        <input type="checkbox" class="access-table-input car-park-check" disabled> secondary checkbox
                                    </label>
      </td>
      <td class="base-td access-td four">
        <label>
                                        <input type="checkbox" class="access-table-input car-park-check" disabled> secondary checkbox
                                    </label>
      </td>
      <td class="base-td access-td five">
        <label>
                                        <input type="checkbox" class="access-table-input car-park-check" disabled> secondary checkbox
                                    </label>
      </td>
      <td class="base-td access-td six">
        <label>
                                        <input type="checkbox" class="access-table-input car-park-check" disabled> secondary checkbox
                                    </label>
      </td>
    </tr>
  </tbody>
</table>

CodePudding user response:

let carParkCheckMain = document.querySelector('.car-park-check-main');
let carParkChecks = document.querySelectorAll('.car-park-check');
let tdAll = document.querySelectorAll('.access-td');

// Uncheck all checkboxes
for (let i = 0; i < carParkChecks.length; i  ) {
    disable(carParkChecks[i]);
}

// Run through
for (let td of tdAll) {
    td.addEventListener('mouseenter', () => {
        td.addEventListener('click', () => {
            if (carParkCheckMain.checked) {
                for (let i = 0; i < carParkChecks.length; i  ) {
                    enable(carParkChecks[i]); 
                    td.classList.add('td-active');
                }
            } else {
                for (let i = 0; i < carParkChecks.length; i  ) {
                    disable(carParkChecks[i]);
                    td.classList.remove('td-active')
                }
            }
            
        })
        
    })
}

// carParkCheckMain.addEventListener('click', () => {
    
    
// })

function disable (checkbox) {

    return checkbox.disabled = true;
}

function enable (checkbox) {
checkbox.style.backgroudColor = 'green';

    return checkbox.disabled = false;
}

function greenBg(element) {
    element.classList.toggle('td-active');
}
.td-active {
  background-color: rgb(95, 241, 95) !important;
}
<table class="table table-bordered access-table">
                        <tbody>
                            <tr>
                                <th scope="row" style="font-weight: 300;">Checkboxes</th>
                                <td class="main-td access-td one">
                                    <label>
                                        <input type="checkbox" class="access-table-input car-park-check-main"> main checkbox
                                    </label>
                                </td>
                                <td class="base-td access-td two">
                                    <label>
                                        <input type="checkbox" class="access-table-input car-park-check" disabled> secondary checkbox
                                    </label>
                                </td>
                                <td class="base-td access-td four">
                                    <label>
                                        <input type="checkbox" class="access-table-input car-park-check" disabled> secondary checkbox
                                    </label>
                                </td>
                                <td class="base-td access-td five">
                                    <label>
                                        <input type="checkbox" class="access-table-input car-park-check" disabled> secondary checkbox
                                    </label>
                                </td>
                                <td class="base-td access-td six">
                                    <label>
                                        <input type="checkbox" class="access-table-input car-park-check" disabled> secondary checkbox
                                    </label>
                                </td>
                            </tr>
                        </tbody>
                    </table>

CodePudding user response:

My suggestion is as below, with explanatory comments in the code of that demonstration:

let carParkCheckMain = document.querySelector('.car-park-check-main'),
  carParkChecks = document.querySelectorAll('.car-park-check'),
  tdAll = document.querySelectorAll('.access-td'),
  allCheckboxes = document.querySelectorAll('table input[type=checkbox]');


// defining a function - cellHighlight() - using an Arrow function
// expression; this function takes two arguments:
// el: Node, the node from which to navigate to the closest
//     ancestor <td> element, and
// state: an expression/assessment which will be evaluated to
//        a true/truthy/false/falsey value to determine the
//        highlighted/unhighlighted state of the <td>:
const cellHighlight = (el, state) => {
  // we use Element.closest() to navigate to the
  // closest ancestor element matching the selector; if
  // the 'el' node matches that selector then the 'el' node
  // will be returned from closest; if there is no matching
  // ancestor then this will return null:
  el.closest('td')
    // we then use Element.classList API to update the classList:
    .classList
    // we use the toggle() method to apply, or remove, the
    // 'td-active' class based on the state being truthy/true
    // (it will be added) or falsey/false (the class would be
    // removed):
    .toggle('td-active', state)
};

// binding the anonymous (Arrow) function of the EventTarget.addEventListener()
// method as the event-handler for the 'change' event, and passing in the
// Event Object ('e'):
carParkCheckMain.addEventListener('change', (e) => {
  // caching a reference to the main checkbox, though as
  // we only call it once this isn't really necessary in
  // this demo:
  const main = e.currentTarget,
    // retrieving the checked/unchecked state of the
    // main checkbox for later use:
    currentState = main.checked;
  // using NodeList.prototype.forEach() to iterate over all
  // elements retrieved in the carParkChecks NodeList, and
  // passing the current element-node into the Arrow function:
  carParkChecks.forEach(
    (checkbox) => {
      // updating the enabled/disabled state to the inverse
      // of the checked/unchecked state of the main checkbox;
      // if the main checkbox is checked (main.checked === true)
      // we want to enable the checkbox.disabled state by
      // setting checkbox.disabled = false (inverse of true),
      // and vice-versa:
      checkbox.disabled = !currentState;

      // calling the cellHighlight() function, passing in the current
      // Node and the assessment by which that Node should highlighted
      // or not:
      cellHighlight(checkbox, checkbox.disabled === false && checkbox.checked)
    });
});

// again using NodeList.prototype.forEach(), and Arrow functions,
// to first bind the anonymous event-handling function to the
// 'change' event of each Node in the NodeList.
// I wasn't sure if all checkbox <td> cells - inclusing the 'main'
// checkbox - should have the 'td-active' class, if so then the
// 'carParkChecks' can be replaced with the 'allCheckboxes' NodeList:
carParkChecks.forEach(
  (checkbox) => checkbox.addEventListener('change', (e) => {
    // and then again using the cellHighlight() function
    // to toggle the highlight of the cell as appropriate:
    cellHighlight(e.target, checkbox.checked)
  })
);
.td-active {
  background-color: rgb(95 241 95);
}
<table class="table table-bordered access-table">
  <tbody>
    <tr>
      <th scope="row">Checkboxes</th>
      <td class="main-td access-td one">
        <label>
          <input type="checkbox" class="access-table-input car-park-check-main"> main checkbox
        </label>
      </td>
      <td class="base-td access-td two">
        <label>
          <input type="checkbox" class="access-table-input car-park-check" disabled> secondary checkbox
        </label>
      </td>
      <td class="base-td access-td four">
        <label>
          <input type="checkbox" class="access-table-input car-park-check" disabled> secondary checkbox
        </label>
      </td>
      <td class="base-td access-td five">
        <label>
          <input type="checkbox" class="access-table-input car-park-check" disabled> secondary checkbox
        </label>
      </td>
      <td class="base-td access-td six">
        <label>
          <input type="checkbox" class="access-table-input car-park-check" disabled> secondary checkbox
        </label>
      </td>
    </tr>
  </tbody>
</table>

JS Fiddle demo.

Note that I removed two of your functions, the disable() and enable(), largely this was because they were unnecessary, given that it's a single line of JavaScript to do exactly the same thing; if you really wish to add those back in then, obviously do so.

One of the problems that I saw in your original posted code – as I mentioned in my comment to the question – is that in this particular piece of code:

for (let td of tdAll) {
    td.addEventListener('mouseenter', () => {
        td.addEventListener('click', () => {
          // removed for brevity
            }  
        })       
    })
}

You're binding an anonymous mouseenter event-handler on each <td> element, and within that event-handler you're binding another event-handler to the same element. As you never remove that inner ('click') event-handler you will end up with each <td> having multiple click event-handlers which – were this more complicated code – would eventually cause performance problems on the user's device.

If you were to use a named function, and then remove that function, for example:

for (let td of tdAll) {
    td.addEventListener('mouseenter', () =>
        td.addEventListener('click', clickHandler)
    );
    td.addEventListener('mouseleave', () =>
        td.removeEventListener('click', clickHandler)
    );   
}

But this still seems a little redundant, especially when the change event-handler can be bound to the input element, and will fire in response to the mouse and space-bar events (as well as to other events fired in other assistive technologies).

One refinement I'd personally make is to use custom colours – using CSS properties – to allow for each checkbox-associated <td> to have its own particular colour, which involves updating the CSS to:

.td-active {
  background-color: var(--highlight, fuchsia);
}

And, in this case, updating the inline style attribute of the <td> elements to contain values such as:

  <td class="base-td access-td two" style="--highlight: orange">
    <label>
      <input type="checkbox" class="access-table-input car-park-check" disabled> secondary checkbox
    </label>
  </td>

For example:

let carParkCheckMain = document.querySelector('.car-park-check-main'),
  carParkChecks = document.querySelectorAll('.car-park-check'),
  tdAll = document.querySelectorAll('.access-td'),
  allCheckboxes = document.querySelectorAll('table input[type=checkbox]');


// defining a function - cellHighlight() - using an Arrow function
// expression; this function takes two arguments:
// el: Node, the node from which to navigate to the closest
//     ancestor <td> element, and
// state: an expression/assessment which will be evaluated to
//        a true/truthy/false/falsey value to determine the
//        highlighted/unhighlighted state of the <td>:
const cellHighlight = (el, state) => {
  // we use Element.closest() to navigate to the
  // closest ancestor element matching the selector; if
  // the 'el' node matches that selector then the 'el' node
  // will be returned from closest; if there is no matching
  // ancestor then this will return null:
  el.closest('td')
    // we then use Element.classList API to update the classList:
    .classList
    // we use the toggle() method to apply, or remove, the
    // 'td-active' class based on the state being truthy/true
    // (it will be added) or falsey/false (the class would be
    // removed):
    .toggle('td-active', state)
};

// binding the anonymous (Arrow) function of the EventTarget.addEventListener()
// method as the event-handler for the 'change' event, and passing in the
// Event Object ('e'):
carParkCheckMain.addEventListener('change', (e) => {
  // caching a reference to the main checkbox, though as
  // we only call it once this isn't really necessary in
  // this demo:
  const main = e.currentTarget,
    // retrieving the checked/unchecked state of the
    // main checkbox for later use:
    currentState = main.checked;
  // using NodeList.prototype.forEach() to iterate over all
  // elements retrieved in the carParkChecks NodeList, and
  // passing the current element-node into the Arrow function:
  carParkChecks.forEach(
    (checkbox) => {
      // updating the enabled/disabled state to the inverse
      // of the checked/unchecked state of the main checkbox;
      // if the main checkbox is checked (main.checked === true)
      // we want to enable the checkbox.disabled state by
      // setting checkbox.disabled = false (inverse of true),
      // and vice-versa:
      checkbox.disabled = !currentState;

      // calling the cellHighlight() function, passing in the current
      // Node and the assessment by which that Node should highlighted
      // or not:
      cellHighlight(checkbox, checkbox.disabled === false && checkbox.checked)
    });
});

// again using NodeList.prototype.forEach(), and Arrow functions,
// to first bind the anonymous event-handling function to the
// 'change' event of each Node in the NodeList:
carParkChecks.forEach(
  (checkbox) => checkbox.addEventListener('change', (e) => {
    // and then again using the cellHighlight() function
    // to toggle the highlight of the cell as appropriate:
    cellHighlight(e.target, checkbox.checked)
  })
);
.td-active {
  background-color: var(--highlight, fuchsia);
}
<table class="table table-bordered access-table">
  <tbody>
    <tr>
      <th scope="row">Checkboxes</th>
      <td class="main-td access-td one" style="--highlight: lime">
        <label>
          <input type="checkbox" class="access-table-input car-park-check-main"> main checkbox
        </label>
      </td>
      <td class="base-td access-td two" style="--highlight: orange">
        <label>
          <input type="checkbox" class="access-table-input car-park-check" disabled> secondary checkbox
        </label>
      </td>
      <td class="base-td access-td four" style="--highlight: #ffa">
        <label>
          <input type="checkbox" class="access-table-input car-park-check" disabled> secondary checkbox
        </label>
      </td>
      <td class="base-td access-td five" style="--highlight: hsl(235, 100%, 50%, .2)">
        <label>
          <input type="checkbox" class="access-table-input car-park-check" disabled> secondary checkbox
        </label>
      </td>
      <td class="base-td access-td six">
        <label>
          <input type="checkbox" class="access-table-input car-park-check" disabled> secondary checkbox
        </label>
      </td>
    </tr>
  </tbody>
</table>

JS Fiddle demo.

This is, however, probably only required to support any branding or theming.

References:

  • Related