此事件侦听器的行为是否如D3中所预期的那样



当我试图引用事件处理程序之外的变量时,遇到了一些意外行为。

下面是一个名为update的函数,它将数据与div元素连接起来。在这种情况下,只有一个数据点,因此只呈现一个div元素。该函数还接受一个数字currentState作为参数。div元素有一个click事件监听器,它打印出currentState数字,并将全局变量state递增1。state的原始值为5。然后单击处理程序调用update,并将递增的state作为参数。

单击div时,传递到update中的currentState会按预期递增。这从CCD_ 10的输出中可以明显看出。

但是,在"click"事件处理程序内部,无论div元素被单击多少次,currentState都保持为5。

如果我错了,请纠正我,但在我看来,当"点击"事件侦听器在D3中创建时,它会保留当时存在的任何上下文。而且,因为该监听器只有在第一次进入DOM时才附加到div,所以该上下文永远不会更新。

解决这个问题的一个简单方法就是不在回车选择中附加click监听器,而是在对join的调用之外。这样,每次调用update时都会更新监听器。

我基本上有两个问题。我想确认一下我是否正确地理解了正在发生的事情。第二,D3中的这种行为有利吗?也许我错过了它的一个优点。虽然这个例子有点做作,但我希望点击处理程序引用currentState的值,该值会随着对update的每次调用而更新。还是在输入选择中附加听众不是一种好的做法?

const data = ['randomString']
let state = 5
function update (currentState) {
console.log("update called. currentState: ", currentState)
d3.selectAll('div')
.data(data)
.join(enter => enter.append('div')
.style('width', '50px')
.style('height', '50px')
.style('background', 'red')
.on('click', function () {
console.log("click event. currentState: ", currentState)
state += 1
update(state)
}))
}
update(state)

发生的情况是,您的初始on绑定是d3在您的案例中唯一使用的绑定。让我们分析一下这个代码:

d3.selectAll('div')
.data(data)
// ...
.join(enter => enter.append('div')
.on('click', function () {
console.log("click event. currentState: ", currentState)
state += 1
update(state)
}))

这是在干什么?

  1. 选择DOM中迄今为止存在的所有div
  2. data附加到它们上
  3. 如果div节点少于data条目,则为每个条目创建一个新的div,并附加一个记录currentState的单击处理程序

如果第二次运行此代码会发生什么?在步骤3中,每个数据条目已经有一个div,因此不会创建新的div,也不会附加新的单击处理程序,除非data发生更改。

作为对另一个答案的补充,它正确地解释了D3中发生的事情,这不是D3的行为,这是JavaScript的工作方式。

这里有一个演示,展示了只使用JavaScript而不使用D3的情况。正如在您的示例中创建foo时一样,它取决于其上下文,因此也取决于currentState值。为了与D3代码进行简单比较,foo存储在对象中,因此不会在每次调用update时再次创建。

因此,正如预期的那样,修改currentState值在创建后对foo没有影响:它每次都会记录5,而不是567等。

let state = 5;
const obj = {};
function update(currentState) {
if (!obj.foo) obj.foo = () => console.log(currentState);
obj.foo();
state += 1;
};
update(state);
update(state);
update(state);

最新更新