当我在D3中触发事件中删除相应元素时,如何删除元素的事件侦听器?

时间:2018-03-13 04:51:02

标签: javascript d3.js

当我在D3中触发事件中删除相应元素时,如何删除元素的事件侦听器?

现在,在我致电.remove()之后,事件似乎仍然挥之不去。

例如,我有一个侦听mousedown的事件监听器,如果我一直在使用我的键盘移除相应的元素,那么mousedown事件仍然存在。这是预期的吗?

更具体地说,我正在处理我的元素调用的d3.drag()函数。

如果是这样,有没有办法立即清除所有这些或我是否需要手动删除它们?如何删除挥之不去的事件?

以下是重现的链接:https://jsfiddle.net/38wtj4y0/33/

尝试拖动矩形并不释放您的mousedown 。按一个键,矩形将消失,但当你移动鼠标(仍然是mousedown)时,你会看到控制台仍在打印“拖动”

更新

@Gerardo Furtado下面的回答仍然是一个解决方法,即使用if语句使所有触发函数返回。换句话说,如果用户没有释放mousedown,在场景后面,这个拖动功能仍将永远被触发,只是因为他们将返回它们将不会做任何事情。这仍然是对资源的彻底浪费。

我能想到的唯一解释导致这个问题是D3只能在事件监听器不再活动时删除它,即它必须等待用户释放。因此,我需要一种方法来释放所有触发的用户交互。

1 个答案:

答案 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;
&#13;
&#13;