3

我有以下 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);
    ...
}
4

2 回答 2

3

在循环中创建 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/

于 2013-09-13T14:25:28.567 回答
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 的主体是一个闭包,因此保留了闭包的局部范围。

于 2013-09-13T14:46:01.673 回答