在事件冒泡阶段删除祖先

Removing an ancestor during the event bubbling phase

本文关键字:删除 祖先 段删除 事件      更新时间:2023-09-26

在下面的代码中(JSFiddle在这里(:

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

当您在谷歌浏览器 48 中单击该按钮时,它会触发表单提交。
但是,如果您在 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版本一样。

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

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

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

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

后者应阻止提交表格。

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

因此,当浏览器向窗体触发 submit 事件时,不会取消该事件。

然后浏览器的行为有所不同(演示(:

  • 火狐浏览器不会提交已删除的表单
  • 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();

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

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

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

我认为 Chromium 不会在按下按钮发出处理默认提交操作时检查标记为已销毁的表单。但是 Chromium 会检查 prevent默认标志,其中按钮的默认操作是表单提交。因此,可以在 $('form'(.remove(( 之前或之后添加 event.preventDefault((。

在相反的火狐中,忽略阻止按钮按下事件的默认值,并将控制权传递给form.onsubmit。

因此,这两个浏览器关于 preventDefault 和 onsubmit 的行为完全相反。

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