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>
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>
This is, however, probably only required to support any branding or theming.
References: