A div
element's click event has e.preventDefault()
at the beginning. That makes manually clicking the checkbox input
or it's associated label
inside no longer works.
With the help of some more JavaScript code, manually clicking the label
generates expected results because the checkbox is now programmatically checked/unchecked.
However, manually clicking the checkbox input
still does not work despite the fact that similar JavaScript code for programmatically checking/unchecking has been implemented. Why?
document.querySelector('div').onclick = function (e)
{
e.preventDefault();
if (e.target.tagName.toUpperCase() == 'LABEL')
{
e.target.previousElementSibling.checked = !e.target.previousElementSibling.checked;
}
else if (e.target.tagName.toUpperCase() == 'INPUT')
{
e.target.checked = !e.target.checked;
}
}
<div>
<input id="check" type="checkbox">
<label for="check">Label Text</label>
</div>
Clicking the label
sets the checked
property as expected because there is no default
action related to the label to be canceled. Clicking the input
sets the property as expected, but due to the default action (toggling the checked property) being prevented it reverts to its previous state.
see: Why does preventDefault on checkbox click event returns true for the checked attribute? for more in depth discussion.
document.querySelector('div').onclick = function (e)
{
e.preventDefault();
if (e.target.tagName.toUpperCase() == 'LABEL')
{
e.target.previousElementSibling.checked = !e.target.previousElementSibling.checked;
}
else if (e.target.tagName.toUpperCase() == 'INPUT')
{
console.clear();
console.log(e.target.checked);
e.target.checked = !e.target.checked;
console.log(e.target.checked);
}
}
<div>
<input id="check" type="checkbox">
<label for="check">Label Text</label>
</div>
Edit
In response to your comment I think the cleanest solution would be to explicitly apply listeners to those elements that you want to control outside of the provided API methods calling stopPropagation()
on them to avoid triggering the parent listeners.
You can then handle whatever logic you need without having to work around artifacts of the outside methods. If you need the parent to listener to run as well you can programmatically activate it after your control logic is finished.
// original API listener
document.querySelector('.container').addEventListener('click', function (e) {
e.preventDefault();
// add container specific code
console.log('clicked div');
});
// your isolated listener
document.querySelector('.checkbox-container').addEventListener('click', function (e) {
e.stopPropagation(); // stop the event from propagating to the parent listener
// add checkbox specific code
console.clear();
console.log('clicked checkbox container');
// even programmatically 'clicking' parent if you need to
e.currentTarget.parentElement.click();
});
.container {
width: 150px;
height: 60px;
background-color: lightgray;
}
.checkbox-container {
display: inline-block;
background-color: aquamarine;
}
<div class="container">
<div class="checkbox-container">
<input id="check" type="checkbox">
<label for="check" >Label Text</label>
</div>
</div>