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.

Original source