【问题标题】:Can't check radio button programmatically with preventDefault()无法使用 preventDefault() 以编程方式检查单选按钮
【发布时间】:2019-04-13 21:25:17
【问题描述】:

我有一些元素(例如带有 .label 类的 div),其中每个元素都带有单选按钮。当用户单击此“标签”时,我以编程方式将其中的单选设置为选中状态。但是,如果我使用 preventDefault() 作为点击事件,如果用户完全点击了收音机,则不会选择收音机。

请帮助我理解这种奇怪的行为。 我知道解决方案,我知道为什么父元素上的 preventDefault() 不允许检查收音机,但我想了解,为什么收音机上的点击事件不允许以编程方式设置其状态。您会看到单击单选按钮会说已选中单选,但事实并非如此。

$(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>

更新。我怎么看这种情况:

  1. 用户点击电台
  2. 首先在收音机上触发事件并将输入设置为选中。
  3. 然后事件冒泡并在.label 上触发
  4. 调用 preventDefault() 会设置一个内部 cancelled 标志。
  5. div 获取类 '.checked' 并将单选设置为再次选中,现在以编程方式。
  6. 事件冒泡,但没有任何反应。
  7. 由于事件已取消,因此不应发生默认操作,复选框将重置为之前的状态。

我说的对吗?

【问题讨论】:

  • 你有什么理由需要使用e.preventDefault()吗? &lt;div&gt; 默认情况下不是可点击元素,因此无论如何都没有与原生点击事件相关的操作/操作。
  • 顺便说一句,使用语义。标签应该是label
  • @Terry 我可以删除 e.preventDefault(),但是当我遇到这种情况时,我决定找出,如果单击事件设置为 preventDefault(),为什么我不能以编程方式检查收音机
  • @QuentinVeron 这段代码我简化了,其实结构更复杂

标签: javascript jquery


【解决方案1】:

我怎么看这种情况(灵感来自https://stackoverflow.com/a/15767580/11357125):

  1. 你点击收音机
  2. 已检查
  3. 事件在文档根目录中调度
  4. 捕获阶段,您的处理程序没有任何反应
  5. 事件到达&lt;input&gt;
  6. ……开始冒泡
  7. &lt;div&gt; 上,已处理。事件监听器调用preventDefault 方法,设置一个内部cancelled 标志。 &lt;div&gt; 获取类 '.checked' 和无线电设置为再次检查,现在以编程方式
  8. 事件冒泡,但没有任何反应。
  9. 由于事件已取消,因此不应发生默认操作,并且复选框会重置为之前的状态即使在以编程方式检查后

【讨论】:

    【解决方案2】:

    在父元素上使用 preventDefault() 可以防止触发原始事件,但不会停止传播。了解层次结构和事件传播至关重要。

    您的代码 sn-p 中有部分解决方案。如果您注释掉该特定行,则代码可以正常工作,就像您期望的那样。 但是如果你使用

    e.stopPropagation();
    

    它也有效。

    为了不重复网络上已有的信息,我在这里找到了一个非常相似的案例 (Why does preventDefault() on a parent element's click 'disable' a checkbox?),它可以帮助您更好地理解事件传播和冒泡。

    您还可以在此处找到更好的解释 (https://medium.freecodecamp.org/a-simplified-explanation-of-event-propagation-in-javascript-f9de7961a06e)。

    MDN 文档也很少不能给人留下深刻印象 (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)。

    【讨论】:

    • 谢谢,我想,第一个链接的答案澄清了我的情况。我已经更新了我的问题
    【解决方案3】:

    您只需要添加e.stopPropagation() 即可再次返回单选按钮的默认功能checked

    行为是您拥有radiodiv 的子代,而您click 侦听器基于父代div,当您preventDefault 时,子代也会继承预防措施。

    检查that

    如果按钮在一个元素上被按下并在另一个元素上被释放,则事件会在包含两者的最具体的祖先元素上触发。

    【讨论】:

    • 还是不行。单击单选按钮不检查它?
    • 我认为,您必须在带有 stopPropagation() 的收音机中添加额外的点击处理程序,并以编程方式点击标签。但是,正如我在问题中所说,我知道解决方案,我想了解行为。
    猜你喜欢
    • 1970-01-01
    • 2017-01-27
    • 2012-02-06
    • 1970-01-01
    • 2011-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多