Reputation: 2288
When I click on a node, I want a new node to be added to it. They should both have labels (I'm trying to build a thesaurus visualization).
I'm very new to D3, so I apologize if you have to explain things in a bit more detail.
This is my code so far:
var width = 960;
var height = 500;
var force = d3.layout.force()
.gravity(0.05)
.distance(100)
.charge(-100)
.size([width, height])
.nodes([{ "name": "One", "group": 1 }])
.start();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var nodes = force.nodes();
var links = force.links();
var link = svg.selectAll(".link")
.data(links)
.enter().append("line")
.attr("class", "link");
var node = svg.selectAll(".node")
.data(force.nodes())
.enter().append("g")
.attr("class", "node")
.call(force.drag);
node.append("circle")
.attr("r", 10)
.on("mousedown", onClick);
node.append("text")
.attr("dx", 12)
.attr("dy", ".35em")
.text(d => d.name);
force.on("tick", function() {
link.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);
node.attr("transform", d => {
// d.x & d.y are NaN for new nodes
return "translate(" + d.x + "," + d.y + ")";
});
});
restart();
function restart() {
node = node.data(nodes);
node.enter().append("g")
.attr("class", "node")
.call(force.drag);
node.append("circle")
.attr("r", 10)
.on("mousedown", onClick);
node.append("text")
.attr("dx", 12)
.attr("dy", ".35em")
.text(d => d.name);
node.exit().remove();
link = link.data(links);
}
function onClick(clicked_node) {
console.log("click!");
console.log(clicked_node);
var new_node = { name: "Test", group: 2 };
nodes.push(new_node);
// Has x & y set to NaN after adding
links.push({ source: clicked_node, target: new_node });
restart();
}
As soon as I click on the first node, causing the node
{ name: "Test", group: 2 };
to be added, D3 throws errors within
node.attr("transform", d => {
// d.x & d.y are NaN for new nodes
return "translate(" + d.x + "," + d.y + ")";
});
because the d.x
and d.y
for this new node are NaN
.
I tried setting them explicitly:
{ name: "Test", group: 2, x: clicked_node.y, y: clicked_node.y };
But I get the same error. In the inspector, when this node is added to the screen, the x
and y
values becomes the px
and py
values instead!
I don't understand why this happens.
Upvotes: 2
Views: 1078
Reputation: 108512
You are missing one line of code. After you add your new node you need to restart the simulation for d3
to calculate it's position:
function restart() {
node = node.data(nodes);
node.enter().append("g")
.attr("class", "node")
.call(force.drag);
node.append("circle")
.attr("r", 10)
.on("mousedown", onClick);
node.append("text")
.attr("dx", 12)
.attr("dy", ".35em")
.text(d => d.name);
node.exit().remove();
link = link.data(links);
force.start(); //<-- start simulation
}
Running code:
<!DOCTYPE html>
<html>
<head>
<script data-require="[email protected]" data-semver="3.5.17" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.js"></script>
</head>
<body>
<script>
var width = 960;
var height = 500;
var force = d3.layout.force()
.gravity(0.05)
.distance(100)
.charge(-100)
.size([width, height])
.nodes([{ "name": "One", "group": 1 }])
.start();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var nodes = force.nodes();
var links = force.links();
var link = svg.selectAll(".link")
.data(links)
.enter().append("line")
.attr("class", "link");
var node = svg.selectAll(".node")
.data(force.nodes())
.enter().append("g")
.attr("class", "node")
.call(force.drag);
node.append("circle")
.attr("r", 10)
.on("mousedown", onClick);
node.append("text")
.attr("dx", 12)
.attr("dy", ".35em")
.text(d => d.name);
force.on("tick", function() {
link.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);
node.attr("transform", d => {
// d.x & d.y are NaN for new nodes
return "translate(" + d.x + "," + d.y + ")";
});
});
restart();
function restart() {
node = node.data(nodes);
node.enter().append("g")
.attr("class", "node")
.call(force.drag);
node.append("circle")
.attr("r", 10)
.on("mousedown", onClick);
node.append("text")
.attr("dx", 12)
.attr("dy", ".35em")
.text(d => d.name);
node.exit().remove();
link = link.data(links);
force.start();
}
function onClick(clicked_node) {
console.log("click!");
console.log(clicked_node);
var new_node = { name: "Test", group: 2 };
nodes.push(new_node);
// Has x & y set to NaN after adding
links.push({ source: clicked_node, target: new_node });
restart();
}
</script>
</body>
</html>
Upvotes: 2