Adding and Removing Nodes in D3js Force Graph
d3.js, graph, javascript
Solution
Adding nodes/links to my D3 force graph was very confusing until I better understood the way I was adding the initial set of nodes.
Assuming a `<g>` is what you'd like to use for your nodes:
// Select the element where you'd like to create the force layout
var el = d3.select("#svg");
// This should not select anything
el.selectAll("g")
// Because it's being compared to the data in force.nodes()
.data(force.nodes())
// Calling `.enter()` below returns the difference in nodes between
// the current selection and force.nodes(). At this point, there are
// no nodes in the selection, so `.enter()` should return
// all of the nodes in force.nodes()
.enter()
// Create the nodes
.append("g")
.attr("id", d.name)
.classed("manipulateYourNewNode", true);
Now let's make that function that will add a node to the layout once the graph has been initialized!
`newNodeData` is an object with the data you'd like to use for your new node. `connectToMe` is a string containing the unique id of a node you'd like to connect your new node to.
function createNode (newNodeData, connectToMe) {
force.nodes().push(newNodeData);
el.selectAll("g")
.data(force.nodes(), function(datum, index) { return index })
The function given as the optional second argument in `.data()` is run once for each node in the selection and again for each node in `force.nodes()`, matching them up based on the returned value. If no function is supplied, a fallback function is invoked, which returns the `index` (as above).
However, there's most likely going to be a dispute between the index of your new selection (I believe the order is random) and the order in `force.nodes()`. Instead you'll most likely need the function to return a property that is unique to each node.
This time, `.enter()` will only return the node you're trying to add as `newData` because no key was found for it by the second argument of `.data()`.
.enter()
.insert("g", "#svg")
.attr("id", d.name)
.classed("manipulatYourNewNode", true);
createLink(connectToMe, newNodeData.name);
force.start();
}
The function createLink (defined below) creates a link between your new node and your node of choice.
Additionally, the d3js API states that force.start() should be called after updating the layout.
Note: Calling `force.stop()` at the very beginning of my function was a huge help for me when I was first trying to figure out how to add nodes and links to my graph.
function createLink (from, to) {
var source = d3.select( "g#" + from ).datum(),
target = d3.select( "g#" + to ).datum(),
newLink = {
source: source,
target: target,
value: 1
};
force.links().push(newLink);
The code below works under the assumptions that:
- `#links` is the wrapper element that contains all of your link elements
Your links are represented as `<line>` elements:
d3.select("#links")
.selectAll("line")
.data(force.links())
.enter()
.append("line");
Problem
I am loading `json` from database and creating a `json` file which loads fine. Now I don't know which steps to take for making the nodes responsive in a Force-Directed Graph. I need to remove and add new nodes and their links. ``` force.nodes(json.nodes) .links(json.links) .start(); initNodes(json); ``` How can I make this more dynamic or update it without resetting the whole visualization? I have seen this question a couple of times not being answered so I hope someone can post and give a guide.