Skip to content Skip to sidebar Skip to footer

Select Subnode In Ver4, How?

I upgrade my app from d3 - ver3 to d3 - ver4. There is code version 3(and version 4 - comment ver3 and uncomment ver4): In version 4 in code, I don't get html rect object. Why? H

Solution 1:

In version 4 in code, I don't get html rect object. Why?

The behaviour you're seeing using D3 4.x is the correct one.

What happened is that Mike Bostock, the creator of D3, introduced a magic (as he describe it) in D3 v2, that was mantained in D3 v3. When you do this:

var classes = classesG.selectAll("g").data(nodes);

You have a data binding selection. If, at that stage, you console.log(JSON.stringify(classes)) you'll get:

[
    [null, null, null]
]

Now comes the magic: if you do:

var newClass = classes.enter().append("g");
newClass.append("rect");

You will magically change classes variable. You can easily see it by the new result of console.log(JSON.stringify(classes)):

    [
    [{
        "__data__": {
            "id": "n11",
            "name": "Node 1",
            "x": 169,
            "y": 110
        }
    }, {
        "__data__": {
            "id": "n12",
            "name": "Node 2",
            "x": 93,
            "y": 14
        }
    }, {
        "__data__": {
            "id": "n13",
            "name": "Node 3",
            "x": 42,
            "y": 65
        }
    }]
]

So, the enter().append() that came after the data bind selection modified it.

How can I repair my code?

You can merge the selections. However, there is an easier solution: just change:

classes.select("rect")
    .attr("height", function(d, i) {
        return 20;
    })
    .attr("width", function(d, i) {
        return 20;
    });

To:

newClass.select("rect")
    .attr("height", function(d, i) {
        return 20;
    })
    .attr("width", function(d, i) {
        return 20;
    });

Here is a demo:

var nodes = [{
  id: "n11",
  name: "Node 1",
  "x": 169,
  "y": 110
}, {
  id: "n12",
  name: "Node 2",
  "x": 93,
  "y": 14
}, {
  id: "n13",
  name: "Node 3",
  "x": 42,
  "y": 65
}];
var svg = d3.select("body").append("svg");

var classesG = svg.append("g").classed("classes", true);

var classes = classesG.selectAll("g").data(nodes);

var newClass = classes.enter().append("g");
newClass.append("rect");
newClass
  .attr("id", function(d) {
    return d.id
  })
  .classed("node", true)
  .attr("transform", function(d) {
    return "translate(" + d.x + "," + d.y + ")";
  });

newClass.select("rect")
  .attr("height", function(d, i) {
    return 20;
  })
  .attr("width", function(d, i) {
    return 20;
  });
.node {
  fill: #ccc;
  stroke: #000;
  stroke-width: 1.5px;
}
<script src="https://d3js.org/d3.v4.min.js"></script>

Solution 2:

In d3.v4 When joining data the selection returned after joining datarepresents the "update selection" ie, existing elements (in your case classes is empty).

When you call classes.select("rect"), classes is empty so the selection is correctly nothing.

Without the context I can't say what the correct answer for you is, there are two options,

  1. You want to update the new and existing nodes every time, in which case you want the union of the two sets,

    classes.enter().merge(classes).selectAll("rect").attr(..)

  2. You only want to set the rect attrs on new nodes, so you can simply do,

    newClass.append("rect").attr(...)

This is all explained in detail in the changelog

var nodes = [{
  id: "n11",
  name: "Node 1",
  "x": 169,
  "y": 110
}, {
  id: "n12",
  name: "Node 2",
  "x": 93,
  "y": 14
}, {
  id: "n13",
  name: "Node 3",
  "x": 42,
  "y": 65
}];
var svg = d3.select("body").append("svg");

var classesG = svg.append("g").classed("classes", true); 
var classes = classesG.selectAll("g").data(nodes);

var newClass = classes.enter().append("g");
newClass.append("rect");
newClass
  .attr("id", function(d) {
    return d.id
  })
  .classed("node", true)
  .attr("transform", function(d) {
    return "translate(" + d.x + "," + d.y + ")";
  });

console.log(classes.select("rect"));
classes.enter().merge(classes).selectAll("rect")
   .attr("height", function(d, i) {
return 20;
  })
  .attr("width", function(d, i) {
return 20;
  });
.node {
  fill: #ccc;
  stroke: #000;
  stroke-width: 1.5px;
}
<script src="https://d3js.org/d3.v4.min.js"></script>

Post a Comment for "Select Subnode In Ver4, How?"