title: D3 v3 Force Layout - gracefully add/remove nodes without refresh
body: <p><strong>Scenario:</strong></p>
  
  <p>I started with a standard D3 v3 Force Layout 
   that I took from an example online.</p>
  
  <p>I want to enhance this and my aim is:</p>
  
  <ol>
  <li>When script loads call a function to initialise and render graph with some data</li>
  <li>Click a button to update the graph on the page to add/remove nodes and links <strong>gracefully</strong> - i.e. <strong>without a complete re-draw</strong> with nodes 'flying' in.</li>
  </ol>
  
  <p>An example of the type of behaviour I desire is this fantastic graph where dragging the threshold slider 'pops' links in/out without a complete re-render so it is easy to see what has been added/removed: <a href="http://jsfiddle.net/simonraper/TdHgx/?utm_source=website&amp;utm_medium=embed&amp;utm_campaign=TdHgx" rel="nofollow noreferrer">http://jsfiddle.net/simonraper/TdHgx/?utm_source=website&amp;utm_medium=embed&amp;utm_campaign=TdHgx</a></p>
  
  <p><strong>Problem:</strong></p>
  
  <ul>
  <li>I am unsure how to achieve point number 2 above.</li>
  <li>I cannot update my graph with a complete redraw</li>
  <li>I am faily sure the answer is something to do with how I use the D3 general update pattern.</li>
  </ul>
  
  <p>Here is an example of what I have so far:<a href="https://jsfiddle.net/samollason/uvqosxrr/3/" rel="nofollow noreferrer">https://jsfiddle.net/samollason/uvqosxrr/3/</a></p>
  
  <p>Code for jsfiddle:</p>
  
  <hr>
  
  <p><strong>html:</strong></p>
  
  <pre><code>&lt;body&gt;
  
    &lt;button id="update-button1"&gt;Update Data - Remove&lt;/button&gt;
    &lt;button id="update-button2"&gt;Update Data - Add&lt;/button&gt;
  
  &lt;/body&gt;
  </code></pre>
  
  <p><strong>js:</strong></p>
  
  <pre><code>var width = 400,
      height = 500;
  
  var force = d3.layout.force()
      .size([width, height])
      .charge(-400)
      .linkDistance(40)
      .on("tick", tick);
  
  var drag = force.drag()
      .on("dragstart", dragstart);
  
  //Set up the force layout
  var svg = d3.select("body").append("svg")
      .attr("width", width)
      .attr("height", height);
  
  var link = svg.selectAll(".link"),
      node = svg.selectAll(".node");
  
      //we call this function when we first draw graph
      var drawInit = function(graph){
  
      link = link.data(graph.links, function(d) { return d.id; })
          .enter().append("line")
          .attr("class", "link");
  
      node = node.data(graph.nodes, function(d) { return d.id; })
          .enter().append("circle")
          .attr("class", "node")
          .attr("r", 12)
          .on("dblclick", dblclick)
          .call(drag);
  
      force
          .nodes(graph.nodes)
          .links(graph.links)
          .start();
  };
  
      //call this function whenever we want to update the graph
  var update = function(graph){
  
      link = link.data(graph.links, function(d) { return d.id; });
  
      link.exit().remove();
  
      link
          .enter().append("line")
          .attr("class", "link");
  
      node = node.data(graph.nodes, function(d) { return d.id; });
  
      //Remove nodes not in new data set
      node.exit().remove();
  
      //For each datum in dataset that wasn't in old dataset append 
        circle
      node.enter().append("circle")
          .attr("class", "node")
          .attr("r", 12)
          .on("dblclick", dblclick)
          .call(drag);
  
      //Update the force layout graph
      force
          .nodes(graph.nodes)
          .links(graph.links)
          .start();
  };
  
  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) {
      console.log("double clicked on " + d.name);
      d3.select(this).classed("fixed", d.fixed = false);
  }
  
  function dragstart(d) {
      d3.select(this).classed("fixed", d.fixed = true);
  }
  
  //data1 is used for our initial drawing
  data1 = {
      "nodes": [
      {
          "id":0,
          "name": 0,
          "group": 1,
          "size": 10
      },
      {
          "id":1,
          "name": 1,
          "group": 1,
          "size": 10
      },
      {
          "id":2,
          "name": 2,
          "group": 1,
          "size": 20
      },
      {
          "id":3,
          "name": 3,
          "group": 1,
          "size": 30
      },
      {
          "id":4,
          "name": 4,
          "group": 1,
          "size": 25
      }
  ],
      "links": [
      {
          "source": 1, "target": 0, "value":1, "id":0
      },
      {
          "source": 1, "target": 2, "value":1, "id":1
      },
      {
          "source": 1, "target": 3, "value":1, "id":2
      },
      {
          "source": 1, "target": 4, "value":1, "id":3
      }
  ]
  
  };
  
  drawInit(data1);
  
  //When user clicks on button update force layout graph *gracefully*
  d3.select("#update-button1").on("click", function(e) {
  
      var data2 = {
          "nodes": [
              {
                  "id": 0,
                  "name": 0,
                  "group": 1,
                  "size": 10
              },
              {
                  "id": 1,
                  "name": 1,
                  "group": 1,
                  "size": 10
              },
              {
                  "id": 2,
                  "name": 2,
                  "group": 1,
                  "size": 20
              },
              {
                  "id": 3,
                  "name": 3,
                  "group": 1,
                  "size": 30
              }
          ],
          "links": [
              {
                  "source": 1, "target": 0, "value": 1, "id": 0
              },
              {
                  "source": 1, "target": 2, "value": 1, "id": 1
              },
              {
                  "source": 1, "target": 3, "value": 1, "id": 2
              }
          ]
      };
  
          update(data2);
  });
  
  //When user clicks on button update force layout graph *gracefully*
  d3.select("#update-button2").on("click", function(e) {
  
      //this simulates removing a node
      var data3 = {
          "nodes": [
              {
                  "id": 0,
                  "name": 0,
                  "group": 1,
                  "size": 10
              },
              {
                  "id": 1,
                  "name": 1,
                  "group": 1,
                  "size": 10
              },
              {
                  "id": 2,
                  "name": 2,
                  "group": 1,
                  "size": 20
              },
              {
                  "id": 3,
                  "name": 3,
                  "group": 1,
                  "size": 30
              },
              {
                  "id": 4,
                  "name": 4,
                  "group": 1,
                  "size": 30
              },
              {
                  "id": 5,
                  "name": 5,
                  "group": 1,
                  "size": 30
              }
          ],
          "links": [
              {
                  "source": 1, "target": 0, "value": 1, "id": 0
              },
              {
                  "source": 1, "target": 2, "value": 1, "id": 1
              },
              {
                  "source": 1, "target": 3, "value": 1, "id": 2
              },
              {
                  "source": 1, "target": 4, "value": 1, "id": 3
              },
              {
                  "source": 1, "target": 5, "value": 1, "id": 4
              }
          ]
      };
  
      update(data3);
  });
  </code></pre>