无法使用preventDefault()

时间:2019-04-13 21:25:18

标签: javascript jquery

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

请帮助我了解这种奇怪的行为。 我知道解决方案,我知道为什么父元素上的preventDefault()不允许检查单选,但我想理解,为什么单选上的click事件可以不允许以编程方式设置其状态。您会看到单击单选按钮会显示已选中单选,但未选中。

$(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. 由于该事件已取消,因此不应执行默认操作,并且该复选框将重置为其以前的状态。

我对吗?

3 个答案:

答案 0 :(得分:2)

我如何看待这种情况(受https://stackoverflow.com/a/15767580/11357125的启发):

  1. 您点击收音机
  2. 已被检查
  3. 该事件调度在文档根目录上
  4. 捕获阶段,处理程序没有任何反应
  5. 事件到达<input>
  6. ...并开始冒泡
  7. <div>上进行处理。事件侦听器调用preventDefault方法,并设置内部cancelled标志。 <div>将类“ .checked”和广播设置为现在已通过编程方式再次进行检查
  8. 事件冒泡,但是什么也没发生。
  9. 由于该事件已取消,因此默认操作不应该发生,并且即使通过编程方式选中该复选框,该复选框也将重置为以前的状态。

答案 1 :(得分:1)

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

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

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

答案 2 :(得分:0)

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

该行为是,您有radiodiv的子代,而您的click侦听器是基于父级div以及您preventDefault然后孩子们也继承了预防措施。

检查that

  
    

如果在一个元素上按下按钮,然后在另一个元素上释放按钮,则会在包含这两个元素的最特定祖先元素上触发该事件。