I was working on a modal component in Angular and was trying to toggle a class state on a custom input checkbox on clicking on the label, but it didn't work, I thought it was something with material or else with Angular, but also tried with plain HTML and vanilla JS and it does not allow to change or toggle a class in the label. How could this be? Is there something I am missing with the label click behavior? If you click directly on the input it adds the class 'active' but if clicked the 'M' it doesn't. I am hiding the input from the view since I am styling it to look different.
Here is a piece of code for trying here:
window.addEventListener('load', function() {
let elems = document.querySelectorAll('.form-check-label');
elems.forEach((e) => e.addEventListener('click', function() {
console.log(e.classList);
}, false));
}, false);
function checkInput(elem) {
let checkMark = elem.querySelector('input');
checkMark.checked = !checkMark.checked;
}
.form-check-label {
cursor: pointer;
}
<label
onClick = "this.classList.toggle('active');"
for="Monday">
<input
type="checkbox"
id="Monday"
value="Monday" />
<span >
M
</span>
</label>
<br />
<br />
<div
onClick = "this.classList.toggle('active');checkInput(this);"
>
<input
type="checkbox"
value="Monday" />
<span >
M
</span>
</div>
If I change the label for a div, then it works. You can check the class state change in the browser console.
CodePudding user response:
This is a interesting case which seems to be related to the behaviour of input
controlled by label
. It can be observed that the click
on label
generate actually 2 click
events, first coming from span
and second (which seems to be a browser synthetic event nevertheless "real") from input
, which sets the focus on input
element, this is most obvious when using a text
type input
. So, the first click
sets the class while the second removes it, that happens almost instantaneous.
As a simple solution is to discard the click
event generated by input
with event.stopPropagation()
, although it will disable a real click on checkbox
:
window.addEventListener('load', function() {
let elems = document.querySelectorAll('.form-check-label');
elems.forEach((e) => e.addEventListener('click', function() {
console.log(e);
}, false));
}, false);
function checkInput(elem) {
let checkMark = elem.querySelector('input');
checkMark.checked = !checkMark.checked;
}
.form-check-label {
cursor: pointer;
}
<label
onClick = "this.classList.toggle('active');"
for="Monday">
<input
onClick = "event.stopPropagation();"
type="checkbox"
id="Monday"
value="Monday" />
<span >
M
</span>
</label>
<br />
<br />
<div
onClick = "this.classList.toggle('active');checkInput(this);"
>
<input
type="checkbox"
value="Monday" />
<span >
M
</span>
</div>
CodePudding user response:
I suggest that you use the checked
state of the checkbox instead of controlling the checked state manually.
This is different when using Angular or React, but then you most likely control the checked state instead.
let elems = document.querySelectorAll('.form-check-label > input[type="checkbox"]');
elems.forEach((e) => e.addEventListener('change', function() {
if (e.checked) {
e.parentElement.classList.add('active');
} else {
e.parentElement.classList.remove('active');
}
}, false));
.form-check-label {
cursor: pointer;
}
.form-check-label.active {
color: red;
}
<label for="Monday">
<input
type="checkbox"
id="Monday"
value="Monday" />
<span >
M
</span>
</label>