attr不会更改属性,除非我通过匿名函数将其返回

问题描述

var k = -1;
someCircle.attr("cy",someScaleObject(++k));

所有圆都将具有相同的y对齐,而预期的行为是将每个圆绘制在另一个圆的下方。奇怪的是,如果我通过匿名函数返回someScaleObject(++k),则可以达到预期的效果。为什么?作者也声明了d变量,但该匿名函数没有使用它。谁能解释为什么?

// Works as expected
someCircle.attr("cy",function () {
    return someScaleObject(++k);
});

完整代码可在下面找到,该代码摘自 D3中的不耐烦

// keys.js
function makeKeys() {
    var ds1 = [["Mary",1],["Jane",4],["Anne",2]];
    var ds2 = [["Anne",5],3]];

    var scX = d3.scaleLinear().domain([0,6]).range([50,300]),scY = d3.scaleLinear().domain([0,3]).range([50,150]);

    var j = -1,k = -1;

    var svg = d3.select("#keys");

    svg.selectAll("text")
        .data(ds1).enter().append("text")
        .attr("x",20).attr("y",function (d) {
            return scY(++j);
        }).text(function (d) {
            return d[0];
        });

    svg.selectAll("circle").data(ds1).enter().append("circle")
        .attr("r",5).attr("fill","red")
        .attr("cx",function (d) {
            return scX(d[1]);
        })
        .attr("cy",function (d) {
            return scY(++k); // this is what concerns me
        });

    svg.on("click",function () {
        var cs = svg.selectAll("circle").data(ds2,function (d) {
            return d[0];
        });

        cs.transition().duration(1000).attr("cx",function (d) {
            return scX(d[1]);
        })
        cs.exit().attr("fill","blue");
    })
}

还有html文件

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <script src="d3.js"></script>
    <script src="keys.js"></script>
</head>
<body onload="makeKeys()">
    <svg id="keys" width="300" height="150"></svg>

</body>
</html>

解决方法

以下:

someCircle.attr("cy",someScaleObject(++k));

您没有将函数传递给selection.attr(),而是执行someScaleObject(++k)并传递其返回值。如果将第二个参数传递给函数selection.attr()以外的其他函数,则对所有元素使用相同的值-这就是为什么您的圆都位于同一位置的原因。

您可以在不执行函数的情况下传递函数:

someCircle.attr("cy",someScaleObject);

但是,您想要向该函数传递一些整数,D3将三个变量传递给传递给selection.attr()的函数-数据,当前索引和选择中的DOM节点组。这些按此顺序传递。在您的情况下,您想将其他变量传递给该函数,因此该方法将不起作用。

可以构建一个ScaleScaleObject来从函数内部跟踪或访问k本身-从而无需将其作为参数传递。或者,我们可以将函数嵌套在匿名函数中:

someCircle.attr("cy",function() { return someScaleObject(++k) });

这使我们可以将基准点,索引和节点组以外的参数传递给我们要执行的函数。这样,selection.attr()被传递了一个函数,当对选择中的每个元素执行时,将执行someScaleObject预期的次数。

但是......

尽管上述内容对于D3很有用,但在您的特定情况下,不清楚将k传递给缩放对象的必要性,因为k代表每个元素的索引。所以代替:

var k = -1;

selection.attr("cy",function() { return scY(++k); })

我们可以简单地使用:

selection.attr("cy",function(d,i) { return scY(i); })

因为传递给.attr()的函数的第二个参数是选择中元素的索引。 D3已经对此进行了跟踪。我实际上不知道用例中的索引是需要进行外部跟踪的一种情况。