为什么在调用 event.preventDefault() 后使用 JavaScript 切换复选框/单选按钮会失败?

Why does toggling a checkbox/radiobutton with JavaScript fail after calling event.preventDefault()?

提问人:Fabrício Matté 提问时间:2/25/2014 最后编辑:Brian Tompsett - 汤莱恩Fabrício Matté 更新时间:8/29/2020 访问量:1261

问:

请考虑以下示例代码:

<span>
    <input type="checkbox">
</span>

$('span').click(function(e) {
    e.preventDefault();
    $(':checkbox')[0].checked = true;
});

小提琴

据我所知,这应该发生:

  • preventDefault()应防止浏览器的默认行为选中该复选框,即使事件处理程序附加在 DOM 层次结构的上方。这部分工作正常。
  • 我认为,设置应该可以工作,因为它应该独立于浏览器对我取消的事件的默认操作。这部分似乎有问题,好像影响了它 - 删除它,它按预期工作。.checked = truepreventDefault()preventDefault()

复选框始终处于未选中状态的真正原因是什么?

我已经在 Chrome 33 和 Firefox 27 上进行了测试,所以这似乎不是浏览器错误。

这个问题主要是出于好奇心,想扩展我的 DOM/事件模型知识。我不想要解决方法,而是想知道为什么这个例子失败了。

JavaScript jQuery DOM 复选框 jquery-events

评论

0赞 Satpal 2/25/2014
请参阅单击文本时的 Fiddle 不与默认行为交互e.preventDefault()
0赞 Jai 2/25/2014
@Satpal点击事件必然不会跨越到子项,那么实际发生了什么。
0赞 A. Wolff 2/25/2014
我真的很困惑会发生什么,好问题!
0赞 Fabrício Matté 2/25/2014
@Satpal谢谢,这似乎进一步证明了 kamilkp 之前的回答,由于鼠标事件,该复选框似乎在事件发生后未选中,但不确定 (AFAIK) 点击事件之后没有事件。click
3赞 Musa 2/25/2014
类似 stackoverflow.com/questions/7435142/...

答:

1赞 kamilkp 2/25/2014 #1

您必须同时将 和 并设置选中preventDefaultclickmouseupmouseup

尝试:

$('span').click(function(e) {
    e.preventDefault();
}).mouseup(function(e){
    e.preventDefault();
    $(':checkbox')[0].checked = true;
});

评论

0赞 Jai 2/25/2014
不过,还有其他几种更好的解决方法.....问题是复选框始终未选中的真正原因是什么? 而你还没有回答。
0赞 kamilkp 2/25/2014
您正在调用 span。当您单击输入时,单击事件会在 DOM 中冒泡。在事件侦听器中,它已经发生在 .该复选框被选中,然后不久取消选中,因此您甚至看不到它。preventDefaultspan'sinput
0赞 A. Wolff 2/25/2014
@kamilkp 为什么不选中?
0赞 Fabrício Matté 2/25/2014
@kamilkp是的,这是有道理的,尽管仍然感觉有些不对劲。 在 之前发生,并且 ing 不会使用 : jsfiddle.net/V3pW6/1 修复该行为mouseupclickpreventDefaultmouseupclick
5赞 Nope 2/25/2014 #2

给定您的 HTML:

<span>
    <input type="checkbox">
</span>

和你的代码:

$('span').click(function(e) {
    e.preventDefault();
    $(':checkbox')[0].checked = true;
});

事件冒泡是你在这里的问题。

单击 时,单击事件将传播到 . 是对从 传播的事件对象的调用。checkboxspane.preventDefault()checkbox

因此,是针对自身执行的,防止它被检查。preventDefault()checkbox

通过它“取消”的点击事件将保持取消状态,直到事件流完成。(有关更多详细信息,请参阅答案底部)checkboxpreventDefault

如果对跨度应用一些样式,您会注意到单击代码会按预期工作,但由于上述原因,单击代码本身会复制您的问题。checkboxcheckbox


演示 - 原始小提琴 + 样式。点击是好的,不是spancheckbox


根据 preventDefault() 上的 MDN 文档

在事件流的任何阶段调用都会取消 事件,这意味着通常由 由于该事件,不会发生实施。preventDefault

DOM Level 2 规范还指出:

如果事件是可取消的,则该方法用于 表示要取消事件,表示任何默认操作 通常由实施所采取的作为事件的结果不会 发生。如果在事件流的任何阶段,preventDefault 方法 称为事件被取消preventDefault

示例 5 下的 DOM Level 3 事件规范说明:

与元素上的事件关联的默认操作将切换该元素的 IDL 属性值。如果事件的默认操作被取消,则该值将恢复到其以前的状态。click<input type="checkbox">checkedclick

评论

0赞 kamilkp 2/25/2014
这样,就永远不会调用 on 上的事件处理程序。clickspan
0赞 Fabrício Matté 2/25/2014
“因此,preventDefault() 是针对复选框本身执行的,以防止它被选中。” - 是的,这是我预期的行为。问题在于没有对输入应用 preventDefault,问题是为什么设置检查状态的代码行在 之后无法按预期工作。preventDefault()
0赞 Nope 2/25/2014
@FabrícioMatté:它不起作用,因为 span 捕获的第一个事件是复选框中的冒泡点击事件,并且它被告知不做它的默认行为。当您的代码行执行时,事件链仍处于活动状态,并且防止默认行为的指令也处于活动状态。the line of code which sets the checked state does not work
0赞 Fabrício Matté 2/25/2014
猜猜我们沟通得不是很好。让我们尝试另一种方式,我知道单击复选框会冒泡到跨度,而跨度又会点击复选框。问题是,为什么会失败?preventDefault.checked = true
0赞 Nope 2/25/2014
@FabrícioMatté:看看你问题的通讯中链接的 SO 帖子的答案。 这就是我想告诉你的:另一个人解释得更好,尽管:)The misunderstanding probably occurs because you are thinking of the event as firing after the actual click has been fully processed (i.e. the checkbox has been toggled) while in reality the event model stipulates that the handler fires while the event is being processed.By the time your line of code executes the event chain is still active and the instructions to prevent the default behaviour are too
2赞 Sai 2/25/2014 #3

这是我所感受到的预期行为,因为当您说 event.preventDefault() 时,您是在指示浏览器在此事件中不要对其执行任何操作。因此,即使在语句之后,当您明确选中复选框时,浏览器也不会对其执行任何操作。 当我更改事件时,我们可以看到差异如下:

$('span').click(function(e) {
     e.preventDefault();
     //$(':checkbox')[0].checked = true;
     setTimeout(function(){$(':checkbox')[0].checked = true;});
});

使用超时,我们正在更改事件外部的 checked 属性,因此它被检查。

3赞 Alessandro Vendruscolo 2/25/2014 #4

将部分代码包装在 a 中就可以了,正如你在这里看到的 http://jsfiddle.net/y7C77/setTimeout

$('span').click(function(e) {
    e.preventDefault();
    setTimeout(function () {
        $('input').prop('checked', true);
    }, 1);
});

preventDefault()取消默认操作,我认为即使您手动执行浏览器会执行的操作(在本例中:更改属性),它也可以取消它。checked

评论

0赞 Nope 2/25/2014
setTimeout之所以有效,是因为内部函数在事件流完全完成后执行,该事件流调用了传播的事件对象。:)周围做得很好preventDefaultcheckbox
0赞 Fabrício Matté 2/25/2014
我在想同样的事情,但好吧,这是一个理论问题。+1 虽然是一个很好的解决方法,以防将来有人需要它。