我有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);
...
}
答案 0 :(得分:3)
创建javascript functions in a loop有点棘手;在循环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/
答案 1 :(得分:2)
问题与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的主体是一个闭包,因此保留了闭包的局部范围。