最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - D3 - forEach is not a function when upgrading from v3 to v4 - Stack Overflow

programmeradmin0浏览0评论

I am trying to upgrade this stackable bar chart to v4. Everything works except for one thing.

When I filter one category the bars don't drop to the start of the x-axis. I get an error which says:

state.selectAll(...).forEach is not a function

I've tried multiple things but I can't figure this one out. This is the broken code:

function plotSingle(d) {

class_keep = d.id.split("id").pop();
idx = legendClassArray.indexOf(class_keep);

//erase all but selected bars by setting opacity to 0
d3.selectAll(".bars:not(.class" + class_keep + ")")
    .transition()
    .duration(1000)
    .attr("width", 0) // use because svg has no zindex to hide bars so can't select visible bar underneath
    .style("opacity", 0);

//lower the bars to start on x-axis
state.selectAll("rect").forEach(function(d, i) {

    //get height and y posn of base bar and selected bar
    h_keep = d3.select(d[idx]).attr("height");
    y_keep = d3.select(d[idx]).attr("y");

    h_base = d3.select(d[0]).attr("height");
    y_base = d3.select(d[0]).attr("y");

    h_shift = h_keep - h_base;
    y_new = y_base - h_shift;

    //reposition selected bars
    d3.select(d[idx])
        .transition()
        .ease("bounce")
        .duration(1000)
        .delay(750)
        .attr("y", y_new);

})
}

I find it strange that this works flawlessly in D3 v3, why wouldn't this work in v4?

I am trying to upgrade this stackable bar chart to v4. Everything works except for one thing.

When I filter one category the bars don't drop to the start of the x-axis. I get an error which says:

state.selectAll(...).forEach is not a function

I've tried multiple things but I can't figure this one out. This is the broken code:

function plotSingle(d) {

class_keep = d.id.split("id").pop();
idx = legendClassArray.indexOf(class_keep);

//erase all but selected bars by setting opacity to 0
d3.selectAll(".bars:not(.class" + class_keep + ")")
    .transition()
    .duration(1000)
    .attr("width", 0) // use because svg has no zindex to hide bars so can't select visible bar underneath
    .style("opacity", 0);

//lower the bars to start on x-axis
state.selectAll("rect").forEach(function(d, i) {

    //get height and y posn of base bar and selected bar
    h_keep = d3.select(d[idx]).attr("height");
    y_keep = d3.select(d[idx]).attr("y");

    h_base = d3.select(d[0]).attr("height");
    y_base = d3.select(d[0]).attr("y");

    h_shift = h_keep - h_base;
    y_new = y_base - h_shift;

    //reposition selected bars
    d3.select(d[idx])
        .transition()
        .ease("bounce")
        .duration(1000)
        .delay(750)
        .attr("y", y_new);

})
}

I find it strange that this works flawlessly in D3 v3, why wouldn't this work in v4?

Share Improve this question asked Nov 2, 2017 at 15:17 kevin taffeekevin taffee 911 gold badge1 silver badge2 bronze badges 1
  • should state.selectAll instead be d3.selectAll? – Mehrad Commented Nov 2, 2017 at 15:21
Add a ment  | 

1 Answer 1

Reset to default 6

In d3 v3 selectAll returned an array, in d3 v4 it returns an object.

From the v3 notes:

Selections are arrays of elements—literally (maybe not literally...). D3 binds additional methods to the array so that you can apply operators to the selected elements, such as setting an attribute on all the selected elements.

Where as changes in v4 include:

Selections no longer subclass Array using prototype chain injection; they are now plain objects, improving performance. The internal fields (selection._groups, selection._parents) are private; please use the documented public API to manipulate selections. The new selection.nodes method generates an array of all nodes in a selection.

If you want to access each node in v4 try:

selection.nodes().forEach( function(d,i) { ... })

But, this is just the node, to get the data you would need to select each node:

var data = [0,1,2];

var svg = d3.select("body").append("svg")
  .attr("width",500)
  .attr("height",200)

var circles = svg.selectAll("circle")
  .data(data)
  .enter()
  .append("circle")
  .attr("cx", function(d,i) { return i * 20 + 50 })
  .attr("cy", 50)
  .attr("r", 4);
  
  
circles.nodes().forEach(function(d,i) {
    console.log(d3.select(d).data());
})
<script src="https://cdnjs.cloudflare./ajax/libs/d3/4.10.0/d3.min.js"></script>

But, if you need the data or to modify the selection properties, it could be easier to use selection.each(). d3.each iterates through each element of a d3 selection itself, and allows you to invoke a function for each element in a selection (see API docs here):

var data = [0,1,2];

var svg = d3.select("body").append("svg")
  .attr("width",500)
  .attr("height",200)

var circles = svg.selectAll("circle")
  .data(data)
  .enter()
  .append("circle")
  .attr("cx", function(d,i) { return i * 20 + 50 })
  .attr("cy", 50)
  .attr("r", 4);
  

  
circles.each( function() {
  console.log(d3.select(this).data());  
});
<script src="https://cdnjs.cloudflare./ajax/libs/d3/4.10.0/d3.min.js"></script>


In v3 of this bar chart, in the forEach loop

`states.selectAll("rect").forEach(function(d,i) {`

d is an array of nodes (the rectangles in each .g).

But, in v4 d3 selections aren't arrays, you can't use a forEach loop in the same way. But you can still get the nodes in it without much modification using selection.nodes() and than get the childNodes to replicate the array in the v3 version:

state.nodes().forEach(function(d, i) {
            var nodes = d.childNodes;

Here we go through each element/node in state and get the child rects, returned as an array. Here's an updated fiddle.

发布评论

评论列表(0)

  1. 暂无评论