I am trying to update data for a force simulation in D3 v4, similar to this unsuccessful attempt and semi-similar to this successful one . CodePen here
Instead of joining the new nodes it appears to be doubling all the nodes (see pictures). It does not seem to be refreshing the graph correctly either.
HTML:
<button onclick="addData()">Add Data</button>
<svg width="960" height="600"></svg>
JavaScript:
function addData() {
graph.nodes.push({"id": "Extra1", "group": 11},{"id": "Extra2", "group": 11})
graph.links.push({"source": "Extra1", "target": "Valjean", "strength": 1},{"source": "Extra1", "target": "Extra2", "strength": 2})
update()
simulation.restart()
}
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var link, linkEnter, nodeWrapper, nodeWrapperEnter;
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2))
.on("tick", ticked);
var allLinkG = svg.append("g")
.attr("class", "allLinkG")
var allNodeG = svg.append("g")
.attr("class", "allNodeG")
update()
simulation.restart()
function update(){
link = allLinkG
.selectAll("line")
.data(graph.links, function(d){ return d.id })
link.exit().remove()
linkEnter = link
.enter().append("line");
link = linkEnter.merge(link).attr("class","merged");
nodeWrapper = allNodeG
.selectAll("nodeWrapper")
.data(graph.nodes, function(d) { return d.id; })
nodeWrapper.exit().remove();
nodeWrapperEnter = nodeWrapper.enter()
.append("g").attr("class","nodeWrapper")
.append("circle")
.attr("r", 2.5)
nodeWrapper = nodeWrapperEnter
.merge(nodeWrapper).attr("class","merged");
simulation
.nodes(graph.nodes);
simulation.force("link")
.links(graph.links);
}
function ticked() {
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; });
nodeWrapper
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
Thanks very much for any help.
A few issues here:
selectAll
is selecting by element type nodeWrapper
, you meant by class .nodeWrapper
. .merge
, you can't do this as it breaks future selections by .nodeWrapper
class. .merge
your selection, you are merging circle
s with g
s. You should stay consistent and operate on the g
s only. Quick refactor:
function update() {
link = allLinkG
.selectAll("line")
.data(graph.links, function(d) {
return d.id
})
link.exit().remove()
linkEnter = link
.enter().append("line");
link = linkEnter.merge(link).attr("class", "merged");
nodeWrapper = allNodeG
.selectAll(".nodeWrapper") //<-- class nodeWrapper
.data(graph.nodes, function(d) {
return d.id;
})
nodeWrapperEnter = nodeWrapper.enter()
.append("g").attr("class", "nodeWrapper"); //<-- enter selection should be gs
nodeWrapperEnter //<-- append your circles
.append("circle")
.attr("r", 2.5)
nodeWrapper = nodeWrapperEnter //<-- merge, don't change class
.merge(nodeWrapper);
nodeWrapper.exit().remove(); //<-- and the exit
simulation
.nodes(graph.nodes);
simulation.force("link")
.links(graph.links);
}
Note , I also modified your tick
function to operate on the g
instead of the circle
:
nodeWrapper
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
Full code is here .
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.