Javascript 如果删除DOM元素,是否有任何以该元素开头的事件继续冒泡?

Javascript 如果删除DOM元素,是否有任何以该元素开头的事件继续冒泡?,javascript,dom,dom-events,event-bubbling,Javascript,Dom,Dom Events,Event Bubbling,如果我删除了一个用于启动事件气泡的DOM元素,或者是其子元素启动了事件气泡,我应该期待什么样的行为?如果删除该元素,它会继续气泡吗 例如,假设您有一个表,并且希望检测表单元格上的单击事件。另一段JS执行了一个AJAX请求,一旦请求完成,该请求最终将完全替换该表 如果单击该表,并在该表被成功完成AJAX请求替换后立即执行,会发生什么情况?我这样问是因为我看到一些行为,点击事件似乎没有冒泡,但很难复制 我在表的父元素上观看事件(而不是将事件附加到每个TD),有时它似乎无法到达 编辑:再次遇到此问题,

如果我删除了一个用于启动事件气泡的DOM元素,或者是其子元素启动了事件气泡,我应该期待什么样的行为?如果删除该元素,它会继续气泡吗

例如,假设您有一个表,并且希望检测表单元格上的单击事件。另一段JS执行了一个AJAX请求,一旦请求完成,该请求最终将完全替换该表

如果单击该表,并在该表被成功完成AJAX请求替换后立即执行,会发生什么情况?我这样问是因为我看到一些行为,点击事件似乎没有冒泡,但很难复制

我在表的父元素上观看事件(而不是将事件附加到每个TD),有时它似乎无法到达


编辑:再次遇到此问题,并最终找到了问题的根源。这根本不是一个令人兴奋的事件!有关详细信息,请参见下面的答案。

是的,它应该会继续传播。除了
target
属性外,事件与它们激发的事件没有实际的附件。删除元素时,传播事件的内部代码不应“意识到”原始元素已从可见文档中消失

另外,使用
removeChild
不会立即删除元素,它只是将元素从文档树中分离出来。只有在没有对元素的引用时,才应删除/垃圾收集该元素。因此,该元素仍有可能通过
event.target
属性被引用,甚至在被垃圾收集之前被重新插入。我还没试过,所以这只是猜测


T.J.克劳德的评论让我决定举一个简单的例子。我在这两个方面都是对的,它确实会冒泡,您仍然可以使用
event.target
获取对已删除节点的引用


正如T.J.所发现的,IE中并非如此,而是DOM级别2事件规范[emphasis mine]:

指定为冒泡的事件最初将以与非冒泡事件相同的事件流进行。事件被调度到其目标EventTarget,并触发在那里找到的任何事件侦听器。冒泡事件随后将触发通过向上跟踪EventTarget的父链找到的任何其他事件侦听器,检查在每个后续EventTarget上注册的任何事件侦听器。此向上传播将持续到文档,包括文档。在此阶段将不会触发注册为捕获器的EventListeners从事件目标到树顶部的事件目标链在事件初始分派之前确定。如果在事件处理期间对树进行了修改,则事件流将根据树的初始状态继续进行。


根据经验:这取决于您使用的浏览器;IE取消了活动,其他一切(据我所知)都在继续。请参阅下面的测试页面和讨论

理论上:这很有帮助,因为冒泡应该基于树的初始状态。所以大多数人的行为是正确的。让我感到惊讶

但是:这是否与你所看到的有关是另一个问题。您正在监视表的父元素上的单击,您怀疑的是,当您单击表时,很少会出现带有Ajax完成的竞争条件来替换表,并且单击会丢失。这种竞争条件在Javascript解释器中不存在,因为目前浏览器上的Javascript是单线程的。(工作线程即将到来,尽管-哇哦!)但理论上,点击可能会发生,并被浏览器中的非Javascript UI线程排队,然后ajax可以完成并替换元素,然后排队的UI事件得到处理,根本不会发生,或者不会冒泡,因为元素不再有父元素,被移走的。这是否真的会发生在很大程度上取决于浏览器的实现。如果您在任何开源浏览器上看到它,您可能会查看它们的源代码,以便将UI事件排队等待解释器处理。但这与我在下面介绍的在事件处理程序中实际删除带有代码的元素是不同的

泡沫是否继续的实证结果:

测试了Chrome 4和Safari 4(例如WebKit)、Opera 10.51、Firefox 3.6、IE6、IE7和IE8。IE是唯一一个在您删除元素时取消事件的元素(并且在各个版本中都是如此),其他人都没有。无论您使用的是DOM0处理程序还是更现代的处理程序,这似乎并不重要

更新: 测试时,IE9和IE10将继续事件,因此IE不符合规范的情况将在IE8停止

使用DOM0处理程序的测试页:

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>Test Page</title>
<style type='text/css'>
body {
    font-family: sans-serif;
}
#log p {
    margin:     0;
    padding:    0;
}
</style>
<script type='text/javascript'>
window.onload = pageInit;

function pageInit() {
    var parent, child;

    parent = document.getElementById('parent');
    parent.onclick = parentClickDOM0;
    child = document.getElementById('child');
    child.onclick = childClickDOM0;
}

function parentClickDOM0(event) {
    var element;
    event = event || window.event;
    element = event.srcElement || event.target;
    log("Parent click DOM0, target id = " + element.id);
}

function childClickDOM0(event) {
    log("Child click DOM0, removing");
    this.parentNode.removeChild(this);
}

function go() {
}

var write = log;
function log(msg) {
    var log = document.getElementById('log');
    var p = document.createElement('p');
    p.innerHTML = msg;
    log.appendChild(p);
}

</script>
</head>
<body><div>
<div id='parent'><div id='child'>click here</div></div>
<hr>
<div id='log'></div>
</div></body>
</html>

我已经有一段时间没有发帖了。尽管T.J.Crowder的回答很有启发性(Andy E的回答也是如此),并告诉我这应该有效,但我仍然看到了一个问题。我把它放在一边一段时间,但今天当我在另一个web应用程序中再次遇到同样的问题时,我又重新审视了它

我玩了一会儿,终于意识到如何每次都重复这个问题(至少在FF3.6和Chrome8中是这样)。问题不在于删除DOM元素时事件气泡被取消或丢失。相反,问题是如果元素在mousedown和mouseup之间更改,“click”不会触发

根据:

当用户单击元素时,将引发click事件。单击事件将在mousedown和mouseup事件之后发生

<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-type" content="text/html;charset=UTF-8"> <title>Test Page</title> <style type='text/css'> body { font-family: sans-serif; } #log p { margin: 0; padding: 0; } </style> <script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/prototype/1.6.1.0/prototype.js'></script> <script type='text/javascript'> document.observe('dom:loaded', pageInit); function pageInit() { var parent, child; parent = $('parent'); parent.observe('click', parentClick); child = $('child'); child.observe('click', childClick); } function parentClick(event) { log("Parent click, target id = " + event.findElement().id); } function childClick(event) { log("Child click, removing"); this.remove(); } function go() { } var write = log; function log(msg) { $('log').appendChild(new Element('p').update(msg)); } </script> </head> <body><div> <div id='parent'><div id='child'>click here</div></div> <hr> <div id='log'></div> </div></body> </html>