当我试图引用事件处理程序之外的变量时,遇到了一些意外行为。
下面是一个名为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)
}))
这是在干什么?
- 选择DOM中迄今为止存在的所有
div
- 将
data
附加到它们上 - 如果
div
节点少于data
条目,则为每个条目创建一个新的div
,并附加一个记录currentState
的单击处理程序
如果第二次运行此代码会发生什么?在步骤3中,每个数据条目已经有一个div,因此不会创建新的div
,也不会附加新的单击处理程序,除非data
发生更改。
作为对另一个答案的补充,它正确地解释了D3中发生的事情,这不是D3的行为,这是JavaScript的工作方式。
这里有一个演示,展示了只使用JavaScript而不使用D3的情况。正如在您的示例中创建foo
时一样,它取决于其上下文,因此也取决于currentState
值。为了与D3代码进行简单比较,foo
存储在对象中,因此不会在每次调用update
时再次创建。
因此,正如预期的那样,修改currentState
值在创建后对foo
没有影响:它每次都会记录5
,而不是5
、6
、7
等。
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);