D3 onClick 处理程序在执行时似乎具有错误的范围
D3 onClick handler seems to have wrong scope when executed
我有以下用于 D3 的 JavaScript 代码,在我看来它的行为很奇怪。由于我是 D3 的新手,我可能会错过一些东西,但我根本不知道。问题由警报呼叫标记:
我循环访问一组数据集,这些数据集被绘制为线条(从代码中剥离)和这些行上的圆圈/点。当我单击圆圈时,我想在 onClick 处理程序中执行一些代码。onClick 处理程序是传递给 D3 on() 方法的更紧密定义的每个循环迭代。到目前为止,非常正常的JavaScript内容。但:
第一个警报始终显示我所期望的正确值。我将 chartDataItem 的值绑定到 onClick 闭包,因此它在调用时应该具有相同的值。但是如果执行 onClick,我总是从上次迭代中获取值。
D3 on() 方法有什么我看不到的特殊之处吗?
for(var i=0; i<chartData.length; i++){
var chartDataItem = chartData[i];
var currentData = chartDataItem["data"];
alert(chartDataItem["name"]); // <- displays the correct value per iteration
var onClick = function(d){
alert(chartDataItem["name"]); // <- should bind chartDataItem to the scope of the closure?!
}
var dataLines = lineChart.dataLinesGroup.selectAll('.data-line color' + i)
.data([currentData]);
// Draw the lines
...
// Draw the points
lineChart.dataCirclesGroup = lineChart.svg.append('svg:g');
var circles = lineChart.dataCirclesGroup.selectAll('.data-point color' + i)
.data(currentData);
circles
.enter()
.append('svg:circle')
.attr('class', 'data-point color' + i)
.style('opacity', 1e-6)
... more attr and style calls ...
.on("click", onClick)
.transition()
.duration(0)
.attr('cx', function(d) { return x(d.title) })
.attr('cy', function(d) { return y(d.value) })
.style('opacity', 1);
...
}
在循环中创建javascript函数有点棘手;在循环的每次迭代中,chartDataItem
都会被重新定义。由于每个 onClick 函数都会查看要提醒的文本chartDataItem
,因此每次都会提醒chartDataItem
的最后一个值。您可以通过运行来验证这一点
chartDataItem = 'test alert'
循环运行后,单击任何圆圈。
要解决此问题,可以使用闭包来防止修改每个函数的警报文本:
var onClick = function(alertText){
return function(){ alert(alertText); };
}(chartDataItem['name']);
或者,使用 .forEach() 代替 for 循环可以通过在其自己的函数中执行循环的每个循环来避免此问题。
更好的解决方案:http://bost.ocks.org/mike/nest/
这个问题与 for 循环主体不是闭包有关,所以每次循环时你都会重新定义东西,它最终在循环的最后一次迭代中使用变量的定义。
下面是一个关于问题的简化版本的小提琴,它使用 forEach: http://jsfiddle.net/reblace/GxyK6/
var chartData = [{data: "one"}, {data: "two"}, {data: "three"}];
var body = d3.select("body");
chartData.forEach(function(d){
var chartDataItem = d;
var onClick = function(d){
alert(chartDataItem.data);
}
var div = body.append("div");
var words = div.selectAll("div").data([chartDataItem]);
words.enter().append("div").text(function(d) { return d.data; } ).on("click", onClick);
});
使用 forEach,forEach 的主体是一个闭包,因此保留闭包的局部范围。
- 回复'js'仅当请求有错误时(否则使用html)
- 表单已发送,但验证有错误
- 如果文件不存在,fs.watch是否有错误处理程序
- Meteor上有错误的同步问题
- 表单验证:如果有错误,不要't使手风琴动起来
- 我有错误文档.注册元素未被细化
- 如何转换为特定于 json 的类 c#?我有错误
- 如果当前输入有错误或警告,则禁用所有下一个表单输入字段
- 通过有错误的 Ajax-Request 打印出 HTML 会阻止进一步的 JS 工作
- 脸书应用程序有错误
- XDomainRequest 总是有错误
- HTML5 Canvas游戏有错误,点击时第一个拼图块会变成不同的块
- Page_ClientValidate返回 false,但没有一个验证器有错误
- 量角器由中继器命令中是否有错误
- Jquery 验证器 - 表单被保存但有错误
- 任何人看到我的重置功能有错误
- 现在我有错误“;ReferenceError:项未定义“;不知道如何测试我的dataService
- 有没有一种方法可以在加载页面后检查iframe是否有错误消息
- 我可能对hoverIntent有错误的想法
- jQuery插件对象:通过.on()附加了一个事件处理程序,现在有一个范围问题.(主插件对象)