D3.js attr()元';除非通过匿名函数返回属性,否则不能更改属性

D3.js attr()元';除非通过匿名函数返回属性,否则不能更改属性,d3.js,D3.js,所有圆都将具有相同的y对齐,而预期的行为是将每个圆绘制在另一个圆的下方。奇怪的是,如果我通过一个匿名函数返回someScaleObject(++k),这就达到了预期的效果。为什么?作者还在匿名函数的范围内声明了一个d变量,尽管它没有任何用处。有人能解释为什么吗 var k = -1; someCircle.attr("cy", someScaleObject(++k)); 完整的代码可以在下面找到,并从D3中为不耐烦的人绘制 和html文件 // keys.js funct

所有圆都将具有相同的y对齐,而预期的行为是将每个圆绘制在另一个圆的下方。奇怪的是,如果我通过一个匿名函数返回
someScaleObject(++k)
,这就达到了预期的效果。为什么?作者还在匿名函数的范围内声明了一个
d
变量,尽管它没有任何用处。有人能解释为什么吗

var k = -1;
someCircle.attr("cy", someScaleObject(++k));
完整的代码可以在下面找到,并从D3中为不耐烦的人绘制

和html文件

// keys.js
function makeKeys() {
    var ds1 = [["Mary", 1], ["Jane", 4], ["Anne", 2]];
    var ds2 = [["Anne", 5], ["Jane", 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");
    })
}

如下所示:

<!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>

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

您可以传递函数而不执行它:

someCircle.attr("cy", someScaleObject(++k));
但是,如果要将某个整数传递给此函数,D3会将三个变量传递给传递给
selection.attr()
——数据、当前索引和选择中的DOM节点组。这些都是按这个顺序通过的。在您的例子中,您希望向函数传递一些其他变量,因此此方法不起作用

您可以构建一些ScaleObject来跟踪或访问函数中的
k
本身,这样就不需要将其作为参数传递。或者,我们可以将函数嵌套在匿名函数中:

someCircle.attr("cy", someScaleObject);
这允许我们将数据、索引和节点组以外的参数传递给要执行的函数。通过这种方式,
selection.attr()
被传递一个函数,当对选择中的每个元素执行时,执行
someScaleObject
所需的次数

然而

虽然上述内容在D3方面很有用,但在您的特定情况下,将
k
传递给缩放对象的必要性尚不清楚,因为
k
表示每个元素的索引。因此,不是:

someCircle.attr("cy", function() { return someScaleObject(++k) });
我们可以简单地使用:

var k = -1;

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

因为传递给
.attr()
的函数的第二个参数是所选元素的索引。D3已经追踪到了。实际上,我没有意识到索引是需要外部跟踪的东西的用例。

您不是通过
.attr(“cx”,functionName(param))
传递函数,而是传递函数的返回值。这意味着该函数只执行一次。如果要传递函数,可以使用
.attr(“cx”,functionName)
。包装函数允许您在不立即执行的情况下传递参数,允许为选择中的每个元素执行该参数(如果.attr()的第二个参数是函数,则为每个元素执行),这种定位方法似乎不是惯用的-
j
等于元素的索引,这是提供给.attr:
function(d,i){…}
的函数中的第二个参数-因此不清楚为什么声明一个新变量来跟踪索引更可取。至于在函数声明中包含
d
,但不使用它——这可能是在审查中遗漏的习惯,如果不使用它,您可以删除它(前提是不要使用d3传递给函数的其他参数:索引和节点)。使用索引来定位元素,而不是
j
k
。非常好的答案。如何将问题标记为已回答?我可以在一小段时间内发布正确的答案,因为只有答案可以标记为已回答问题,而评论不能。
var k = -1;

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