Nav
Nav

Reputation: 20658

How to dynamically add links to a d3 topology?

I took the sticky force layout example and tried adding an extra link and updating the layout using enter(), but then all the links disappeared and FireBug shows no error either.

Shouldn't these lines have added a link?
graph.links.push({"source": 0, "target": 11});
link = link.data(graph.links).enter().append("line").attr("class", function(d,i) {console.log(i);return "link";});

Also, the console.log(i) outputs 18 instead of 19. It's like as though it never took into account the increase in size of the graph array.

I checked this example, but even here it seems like using the data() and enter() functions should have been enough.

The code:

<!DOCTYPE html>
<meta charset="utf-8">
<style>

.link {
  stroke: #000;
  stroke-width: 1.5px;
}

.node {
  cursor: move;
  fill: #ccc;
  stroke: #000;
  stroke-width: 1.5px;
}

.node.fixed {
  fill: #f00;
}

</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>

var graph =
{
  "nodes": [
    {"x": 469, "y": 410},
    {"x": 493, "y": 364},
    {"x": 442, "y": 365},
    {"x": 467, "y": 314},
    {"x": 477, "y": 248},
    {"x": 425, "y": 207},
    {"x": 402, "y": 155},
    {"x": 369, "y": 196},
    {"x": 350, "y": 148},
    {"x": 539, "y": 222},
    {"x": 594, "y": 235},
    {"x": 582, "y": 185},
    {"x": 633, "y": 200}
  ],
  "links": [
    {"source":  0, "target":  1},
    {"source":  1, "target":  2},
    {"source":  2, "target":  0},
    {"source":  1, "target":  3},
    {"source":  3, "target":  2},
    {"source":  3, "target":  4},
    {"source":  4, "target":  5},
    {"source":  5, "target":  6},
    {"source":  5, "target":  7},
    {"source":  6, "target":  7},
    {"source":  6, "target":  8},
    {"source":  7, "target":  8},
    {"source":  9, "target":  4},
    {"source":  9, "target": 11},
    {"source":  9, "target": 10},
    {"source": 10, "target": 11},
    {"source": 11, "target": 12},
    {"source": 12, "target": 10}
  ]
};

var width = 960,
    height = 500;

var force = d3.layout.force()
    .size([width, height])
    .charge(-400)
    .linkDistance(40)
    .on("tick", tick);

var drag = force.drag()
    .on("dragstart", dragstart);

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

var link = svg.selectAll(".link"),
    node = svg.selectAll(".node");

//d3.json("graph.json", function(error, graph) {

  force.nodes(graph.nodes).links(graph.links).start();
  link = link.data(graph.links).enter().append("line").attr("class", "link");

  node = node.data(graph.nodes)
    .enter().append("circle")
      .attr("class", "node")
      .attr("r", 12)
      .on("dblclick", dblclick)
      .call(drag);
//});

console.log(graph.links.length);
graph.links.push({"source": 0, "target": 11});
console.log(graph.links.length);
link = link.data(graph.links).enter().append("line").attr("class", "link");


function tick() {
  link.attr("x1", function(d) { return d.source.x; })
      .attr("y1", function(d) { return d.source.y; })
      .attr("x2", function(d) { return d.target.x; })
      .attr("y2", function(d) { return d.target.y; });

  node.attr("cx", function(d) { return d.x; })
      .attr("cy", function(d) { return d.y; });
}

function dblclick(d) {
  d3.select(this).classed("fixed", d.fixed = false);
}

function dragstart(d) {
  d3.select(this).classed("fixed", d.fixed = true);
}

</script>

Upvotes: 0

Views: 782

Answers (1)

Lars Kotthoff
Lars Kotthoff

Reputation: 109232

The main problem is

link = link.data(graph.links).enter().append("line").attr("class", "link");

After this, link will contain only the links that were just added to the visualisation as you're using .enter(). This in turn means that only those will be updated during the tick event of the force layout, as you're using the same link variable there. It's the same problem for node.

To fix, set the selection variables to contain all relevant elements after adding the new elements.

link = svg.selectAll(".link");

Complete demo here.

Upvotes: 2

Related Questions