当我在D3中触发事件中删除相应元素时,如何删除元素的事件侦听器?
现在,在我致电.remove()
之后,事件似乎仍然挥之不去。
例如,我有一个侦听mousedown的事件监听器,如果我一直在使用我的键盘移除相应的元素,那么mousedown事件仍然存在。这是预期的吗?
更具体地说,我正在处理我的元素调用的d3.drag()函数。
如果是这样,有没有办法立即清除所有这些或我是否需要手动删除它们?如何删除挥之不去的事件?
以下是重现的链接:https://jsfiddle.net/38wtj4y0/33/
尝试拖动矩形并不释放您的mousedown 。按一个键,矩形将消失,但当你移动鼠标(仍然是mousedown)时,你会看到控制台仍在打印“拖动”
更新
@Gerardo Furtado下面的回答仍然是一个解决方法,即使用if语句使所有触发函数返回。换句话说,如果用户没有释放mousedown,在场景后面,这个拖动功能仍将永远被触发,只是因为他们将返回它们将不会做任何事情。这仍然是对资源的彻底浪费。
我能想到的唯一解释导致这个问题是D3只能在事件监听器不再活动时删除它,即它必须等待用户释放。因此,我需要一种方法来释放所有触发的用户交互。
答案 0 :(得分:2)
这是一个有趣的问题,虽然我不得不承认,我从未在现实世界的应用程序中遇到过这个问题。发生这种情况的原因可以在D3的拖拽实现的内部工作中找到。
首先,值得一提的是,如果从DOM树中删除元素,则在对指针事件执行命中测试时,它不再能成为目标。因此,不会再执行在该元素上注册的事件侦听器。这是你所期望的行为,这也是你混淆的原因,因为在你的JSFiddle中,即使元素被成功删除,监听器也似乎已经执行了。
要了解正在发生的事情,您必须深入了解d3.drag()
的{{3}}。在初始化时,选择元素上的拖动行为source code各种事件处理程序:
function drag(selection) {
selection
.on("mousedown.drag", mousedowned)
//...
}
在对相应元素触发此类事件之前,侦听mousedown
事件的此处理程序不会设置其余的拖动行为。一旦拖动行为的元素收到mousedown
事件,内部registers处理程序将被执行:
function mousedowned() {
//...
select(event.view).on("mousemove.drag", mousemoved, true).on("mouseup.drag", mouseupped, true);
//...
}
在此处理程序中,"mousemove.drag"
和"mouseup.drag"
个侦听器在event.view
上注册。 MouseEvent
的{{3}}属性继承自mousedowned()
接口,并且 - 至少在浏览器中 - 指向事件发生的Window
对象。那些拖动处理程序d3-drag使用全局window
来完成其工作。这些处理程序对您目睹的看似令人困惑的行为负责。我们很快就会谈到这一点,首先让我们检查一下如何删除听众。
当拖动手势最终通过触发mouseup
事件结束时,这些处理程序将从函数window
中的mouseupped()
对象中删除:
function mouseupped() {
select(event.view).on("mousemove.drag mouseup.drag", null);
}
现在,让我们再看看你的代码。即使您删除了由keydown
事件触发的拖动行为的目标,window
上的上述处理程序仍然存在,因为您按住了鼠标按钮,从而将mouseup
事件抑制为被辞退了。因此,mouseupped()
处理程序尚未执行。这将使拖动行为保持活动状态,因为mousemove
上的拖动内部处理程序仍会捕获window
个事件。此外,这些内部处理程序还将继续委派给您自己的dragged
处理程序,从而导致您正在见证的控制台输出。
正如本文开头所提到的,我从未见过这会导致任何现实世界的麻烦。如果您仍想避免此行为,则可以在删除目标后删除内部处理程序:
d3.select(window).on("keydown", function() {
d3.select(".draggable-rect").remove();
d3.select(d3.event.view) // Remove global (internal) drag handlers
.on("mousemove.drag", null)
.on("mouseup.drag", null);
})
在摆弄某些图书馆的内部工作时总是如此,你必须小心谨慎,不要打破其他事情,并记住这有可能在未来任何版本的D3中默默地打破
看看这个有用的演示:
d3.select("svg").append('rect').attr('class', 'draggable-rect');
d3.select(window).on("keydown", function() {
d3.select(".draggable-rect").remove();
d3.select(d3.event.view)
.on("mousemove.drag", null)
.on("mouseup.drag", null);
})
d3.select(".draggable-rect")
.call(d3.drag().on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
function dragstarted(d) {
d3.select(this).raise().classed("active", true);
}
function dragged(d) {
console.log("dragging")
d3.select(this).attr("x", d3.event.x - 40).attr("y", d3.event.y - 40);
}
function dragended(d) {
d3.select(this).classed("active", false);
}

.test-area {
width: 400px;
height: 400px;
border: 1px solid black;
}
svg {
width: 400px;
height: 400px;
}
.draggable-rect {
width: 80px;
height: 80px;
fill: green;
}

<script src="https://d3js.org/d3.v4.js"></script>
<div class="test-area">
<svg>
</svg>
</div>
&#13;