title: d3js force layout with hide/unhide on node click misplaces nodes after expanding
body: <p>I am trying to create a graph using d3 and force layout. I have used the following example <a href="http://bl.ocks.org/mbostock/1062288" rel="nofollow noreferrer">http://bl.ocks.org/mbostock/1062288</a> to get started:</p>
  
  <p><img src="https://i.stack.imgur.com/yxCIH.jpg" alt="enter image description here"></p>
  
  <p>I also need images and labels so I looked at this example <a href="http://bl.ocks.org/mbostock/950642" rel="nofollow noreferrer">http://bl.ocks.org/mbostock/950642</a> to get an idea how I could add them.</p>
  
  <p>My graph will also get bigger depending on the user interactions with the nodes so if a user clicks on a node that doesn't have children, an ajax request will go to the backend service to request more nodes. The graph is meant to be used as a relationship discovery application. I created the following jsfiddle <a href="http://jsfiddle.net/R2JGa/7/" rel="nofollow noreferrer">http://jsfiddle.net/R2JGa/7/</a> to get an idea of what i'm trying to achieve. </p>
  
  <p>It works relatively well but I have one annoying problem: when adding new nodes to the graph the old nodes somehow get misplaced. For instance, I start with 3 nodes, root node is "flare". The other 2 nodes are "animate" and "analytics". In my example the children will always be "x","y","z","t" anytime you click on a node that currently doesn't have children. After expanding a few nodes you will see that "animate" or "analytics" are not linked to the root node "flare" but to some other nodes (x,y,z,t). Or sometimes if you expand x or y or z or t the children nodes have a duplicate x or y or z or t. If you click on "flare" to hide the entire graph and then reopen "flare" you will see that the nodes are correctly linked and named. </p>
  
  <p>I can't seem to see why this is happening. Could somebody shed some light here? I am still new to d3 and find it really interesting but these problems are so annoying...</p>
  
  <p>Here is the code:</p>
  
  <pre><code>var w = 960,
              h = 800,
              node,
              link,
              root;
  
      var force = d3.layout.force()
              .charge(-1000)
              .size([w, h]);
  
      var vis = d3.select("#chart").append("svg:svg")
              .attr("width", w)
              .attr("height", h);
  
      d3.json("data.json", function (json) {
          root = json;
          update();
      });
  
      function update() {
          var nodes = flatten(root);
          nodes.reverse();
          nodes = nodes.sort(function (a, b) {
              return a.index - b.index;
          });
  
          var links = d3.layout.tree().links(nodes);
  
          console.log(nodes);
  
          // Restart the force layout.
          force
                  .nodes(nodes)
                  .links(links)
                  .linkDistance(55)
                  .start();
  
  
          var link = vis.selectAll(".link")
                  .data(links);
  
          link.enter().append("line")
                  .attr("class", "link");
  
          link.exit().remove();
  
          var node = vis.selectAll("g.node")
                  .data(nodes)
  
  
          var groups = node.enter().append("g")
                  .attr("class", "node")
                  .attr("id", function (d) {
                      return d.id
                  })
                  .on('click', click)
                  .call(force.drag);
  
  
          groups.append("image")
                  .attr("xlink:href", "https://github.com/favicon.ico")
                  .attr("x", -8)
                  .attr("y", -8)
                  .attr("width", 16)
                  .attr("height", 16);
  
  
          groups.append("text")
                  .attr("dx", 12)
                  .attr("dy", "0.35em")
                  .style("font-size", "10px")
                  .text(function (d) {
                      console.log(d);
                      return d.name
                  });
  
  
          node.exit().remove();
  
  
          force.on("tick", function () {
              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("transform", function (d) {
                  return "translate(" + d.x + "," + d.y + ")";
              });
          });
      }
  
  
      // Color leaf nodes orange, and packages white or blue.
      function color(d) {
          return d._children ? "#3182bd" : d.children ? "#c6dbef" : "#fd8d3c";
      }
  
      // Toggle children on click.
      function click(d) {
          console.log(d);
          if (d.children) {
              d._children = d.children;
              d.children = null;
              update();
          } else if (d._children) {
              d.children = d._children;
              d._children = null;
              update();
          }
          else {
              d3.json("expand.json", function (json) {
                  d.children = json.children;
                  update();
              })
          }
      }
  
      // Returns a list of all nodes under the root.
      function flatten(root) {
          var nodes = [], i = 0;
  
          function recurse(node) {
              if (node.children) node.children.forEach(recurse);
              if (!node.id) node.id = ++i;
              nodes.push(node);
          }
  
          recurse(root);
          return nodes;
      }
  </code></pre>
  
  <p>And here are the 2 json files I'm requesting:</p>
  
  <p>data.json</p>
  
  <pre><code>{
      "name": "flare",
      "id" : "flare",
      "children": [
          {
              "name": "analytics",
              "id": "analytics"
  
          },
          {
              "name": "animate",
              "id": "animate"
          }
      ]
  }
  </code></pre>
  
  <p>And expand.json</p>
  
  <pre><code>{"children": [
      {
          "name": "x",
          "id": "x",
          "size": 1983
      },
      {
          "name": "y",
          "id": "y",
          "size": 2047
      },
      {
          "name": "z",
          "id": "z",
          "size": 1375
      },
      {
          "name": "t",
          "id": "t",
          "size": 1375
      }
  ]}
  </code></pre>
  
  <p>PS: i had to sort the nodes array otherwise bad things happened to the graph, i cannot understand why. </p>