在事件冒泡阶段删除祖先

Removing an ancestor during the event bubbling phase

在下面的代码中(JSFiddle here):

<form>
   <button>ok</button>
</form>
$(function(){
    $('form').submit(false) ;
    $('button').click(function(){ $('form').remove() }) ;
}) ;

点击GoogleChrome48中的按钮,触发表单提交。
但是,如果您在 Firefox 43 中执行此操作,则不会提交表单。

在我看来,Firefox 的行为应该是正确的,但由于我对标准没有如此深入的了解,所以我真的不知道。

是行为错误还是错误?


跟进:
我刚刚发现相同的测试用例但不使用 jQuery 不会在两个浏览器中触发表单提交。

<form onsubmit="return false">
    <button onclick="form.remove()">ok</button>
</form>

这不可能是时间问题,因为 Javascript 中没有线程并发。事件线程将始终按顺序 运行,因此 button 事件处理程序必须在 form 事件处理程序启动之前完成。
我在这里瞎了。 jQuery 一定是在做一些奇怪的麻烦事才能在 Chrome 中发生。


跟进2:
这不是 jQuery 问题。在 jQuery 错误跟踪器上,我被告知内联事件处理程序不遵循与 addEventListener 附带的规范相同的规范,因此真正的功能等效代码应该是这样的:

<form>
    <button>ok</button>
</form>
<script>
document.querySelector('form').addEventListener('submit',function(){ return false }) ;
document.querySelector('button').addEventListener('click',function(evt){ evt.target.form.remove() }) ;
</script>

这确实像 jQuery 版本。

我认为它依赖于实现并且不能保证相同的行为

当调用 $('form').remove() 时,Chromium 和 Firefox 都不会调用 $('form').submit(...) 安装的 form.onsubmit 处理程序。所以这意味着在 Chromium 和 Firefox 中调用 onsubmit 之前,表单被检查为已销毁(或真正销毁)。

我认为 Chromium 在处理按下按钮发出的默认提交操作时不会检查标记为已销毁的表单。但是 Chromium 会检查 preventDefault 标志,其中按钮的默认操作是表单提交。所以可以在 $('form').remove().

之前或之后添加 event.preventDefault()

在相反的 Firefox 中忽略按钮按下事件的 preventDefault 并将控制权传递给 form.onsubmit。

所以这两个浏览器关于preventDefault和onsubmit的行为是完全相反的。

就当表单被标记为已销毁时提交表单的事实而言,可以想象软件设计中的对象实际上是在通信完成之后而不是之前删除的。但在我看来,这是错误。需要知道开发人员的想法。他们知道吗?这个错误或功能是什么。

您的第一个代码添加了一个 returnFalse jQuery 事件侦听器:

$('form').submit(false);

在 jQuery 事件侦听器中,return false 等同于

event.stopPropagation();
event.preventDefault();

后面应该阻止表单的提交。

但是,在触发 submit 事件之前,您使用 $.fn.remove。这不仅会从文档中删除表单,还会清除其 jQuery 数据,包括事件侦听器。

因此,当浏览器向表单触发 submit 事件时,它不会被取消。

那么浏览器的行为会有所不同 (demo):

  • Firefox 不提交已删除的表单
  • Chrome不关心表单是否被移除,无论如何提交

如果您不想删除 jQuery 数据,您应该使用 vanilla-js 方法而不是 $.fn.remove.

删除表单

在您的第二个代码中,您取消了 vanilla-js 事件处理程序中的事件。

因为它不是jQuery数据,$.fn.remove不会删除它,所以submit事件被取消,表单没有提交。


在你的第三个代码中,你使用 vanilla-js 方法删除了表单,所以它的 jQuery 数据没有被清理。

这没关系,因为 submit 事件侦听器也是使用 vanilla-js 添加的。

但是,活动并未取消。这是因为,与 vanilla-js 事件处理程序和 jQuery 事件监听器不同,vanilla-js 事件监听器中返回的值被完全忽略。

所以最后的结果与第一个代码中的结果相同,但它们并不等价。

如果你想使用 vanilla-js 事件监听器取消事件,你应该使用

event.preventDefault();

这将使它的行为类似于第二个代码。