Reputation: 98
I have some elements (for example divs with class .label) with radio buttons inside of each. When user clicks this "labels" I programmatically set radio in it as selected. But if I use preventDefault() for click event, the radio didn't selected if user clicked exactly on radio.
Please help me to understand this strange behaviour. I know the solution, I know why preventDefault() on parent element disallows to check radio, but I want to understand, why click event on radio can disallow to set its state programmatically. You will see that click on radio button will say that radio is checked, but it's not.
$(function () {
$('.label').on('click', function(e) {
var $radio = $(this).find(':radio')
console.log('before prevent', `checked=${$radio.prop('checked')}`, `prevented=${e.isDefaultPrevented()}`);
e.preventDefault();
if (!$(this).hasClass('checked')) {
$('.checked').removeClass('checked');
$(this).addClass('checked');
}
$radio.prop('checked', true);
console.log('after prevent', `checked=${$radio.prop('checked')}`, `prevented=${e.isDefaultPrevented()}`);
setTimeout(function () {
console.log('after timeout', `checked=${$radio.prop('checked')}`);
}, 500);
});
$(':radio').on('click', function (e) {
console.log('click', `prevented=${e.isDefaultPrevented()}`);
});
});
.label {
padding: 10px;
margin-bottom: 10px;
border: 1px solid #000;
}
.label.checked {
background-color: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="label">
Label 1 <input type="radio" name="radio" value="1">
</div>
<div class="label">
Label 2 <input type="radio" name="radio" value="2">
</div>
UPDATE. How do I see this situation:
.label
cancelled
flag.div
getting class '.checked' and radio setted as checked again, now programmatically.Am I right?
Upvotes: 2
Views: 3725
Reputation: 98
How do I see this situation (inspired by https://stackoverflow.com/a/15767580/11357125):
<input>
<div>
, it is handled. Event listener calls the preventDefault
method, setting an internal cancelled
flag. <div>
getting class '.checked' and radio setted as checked again, now programmatically.Upvotes: 2
Reputation: 24
Using preventDefault() on parent elements prevent the original event to be fired, but does not stop propagation. Understanding hierarchy and event propagation is crucial.
You have part of the solution in your code snippet. If you comment out that particular line, the code works properly, like you would expect. But if you use
e.stopPropagation();
it also works.
In order not to repeat information already on the web, I found a very similar case here (Why does preventDefault() on a parent element's click 'disable' a checkbox?) which may help you understand better event propagation and bubbling.
You can also find an even better explanation here (https://medium.freecodecamp.org/a-simplified-explanation-of-event-propagation-in-javascript-f9de7961a06e).
MDN documentation also rarely fails to impress (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault).
Upvotes: 1
Reputation: 3317
You just need to add e.stopPropagation()
to return the default functionality for radio button again which is checked
The behaviour is that You have radio
is a child to the div
and you click
listener is based on the parent div
and when you preventDefault
then the children inherit the prevention as well.
check that
If the button is pressed on one element and released on a different one, the event is fired on the most specific ancestor element that contained both.
Upvotes: 0