简体   繁体   中英

Can't check radio button programmatically with preventDefault()

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:

  1. User clicks on radio
  2. Firstly event triggered on radio and input setted as checked.
  3. Then event is bubbling up and triggered on .label
  4. Calling preventDefault() sets up an internal cancelled flag.
  5. div getting class '.checked' and radio setted as checked again, now programmatically.
  6. Event bubbles on, but nothing happens any more.
  7. Since the event was cancelled, the default action should not occur and the checkbox is reset to its previous state.

Am I right?

How do I see this situation (inspired by https://stackoverflow.com/a/15767580/11357125 ):

  1. You click on the radio
  2. It gets checked
  3. The event is dispatched on the document root
  4. Capture phase, nothing happens with your handlers
  5. The event arrives at the <input>
  6. …and begins to bubble
  7. On the <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 .
  8. Event bubbles on, but nothing happens any more.
  9. Since the event was cancelled, the default action should not occur and the checkbox is reset to its previous state even after it was checked programmatically .

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 ).

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM