score:8

Accepted answer

You are over complicating the pattern. Here's your update function written properly:

function update(items) {

  var node = nodeLayer.selectAll(".node") // bind the data, this is update
    .data(items, function(d) {
      return d.id;
    });

  node.exit().remove(); // exit, remove the g

  nodeEnter = node.enter() // enter, append the g
    .append("g")
    .attr("class", "node");

  nodeEnter.append("circle") // enter, append the circle on the g
    .attr("r", 2.5)
    .attr('class', 'circle')
    .attr('fill', 'red');

  nodeEnter.append("text") // enter, append the text on the g
    .attr("dy", 3)
    .text(function(d) {
      return d.name;
    })
    .attr('fill', 'green');

  node = nodeEnter.merge(node); // enter + update on the g

  node.attr('transform', function(d){ // enter + update, position the g
    return 'translate(' + d.x + ',' + d.y + ')';
  });

  node.select("text") // enter + update on subselection
    .text(function(d) {
      return d.name;
    });

}

Here it is running with multiple calls:

<!DOCTYPE html>
<html>

<head>
  <script data-require="d3@4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
</head>

<body>
  <script>
  
    var nodeLayer = d3.select('body')
      .append('svg')
      .attr('width',500)
      .attr('height',500);
      
    update([
      {
        id: 1,
        name: 'A',
        x: Math.random() * 500,
        y: Math.random() * 500
      },{
        id: 2,
        name: 'B',
        x: Math.random() * 500,
        y: Math.random() * 500
      },{
        id: 3,
        name: 'C',
        x: Math.random() * 500,
        y: Math.random() * 500
      }
    ]);
    
    setTimeout(function(){
        update([
        {
          id: 1,
          name: 'A',
          x: Math.random() * 500,
          y: Math.random() * 500
        },{
          id: 4,
          name: 'This is a new name...',
          x: Math.random() * 500,
          y: Math.random() * 500
        },{
          id: 3,
          name: 'C',
          x: Math.random() * 500,
          y: Math.random() * 500
        }
      ]);
    }, 3000);
  
    function update(items) {
      
      var node = nodeLayer.selectAll(".node")
        .data(items, function(d) {
          return d.id;
        });
        
      node.exit().remove(); // exit, remove the g

      nodeEnter = node.enter() // enter the g
        .append("g")
        .attr("class", "node");
        
      nodeEnter.append("circle") // enter the circle on the g
        .attr("r", 2.5)
        .attr('class', 'circle')
        .attr('fill', 'red');

      nodeEnter.append("text") // enter the text on the g
        .attr("dy", 3)
        .attr('fill', 'green');
        
      node = nodeEnter.merge(node); // enter + update
          
      node.attr('transform', function(d){
        return 'translate(' + d.x + ',' + d.y + ')';
      });
      
      node.select("text")
       .text(function(d) {
         return d.name;
       });

    }
  </script>
</body>

</html>

score:-1

I've done this recently in my code - I use select(subSelector) on the current selection that contains the elements. In your example I would change it as follows:

function update(items) {
var node = nodeLayer.selectAll(".node")
    .data(items, function(d) { return d.id; })

var nodeEnter = node.enter()
    .append("g")
    .attr("class", "node");

nodeEnter.append("circle")
    .attr("r", 2.5)
    .attr('class', 'circle')
    .merge(node.select('circle'))
    .attr('fill', 'red');

nodeEnter.append("text") // insert
    .attr("dy", 3)
    .text(function(d) { return d.name; })
    .merge(node.select('text'))
    .attr('fill', 'green');

// You only need to call remove on the group, all the other exit().remove() calls are superfluous
node.exit().remove();

simulation
    .nodes(items);

}

score:-1

var svg = d3.select("svg"),
  width = +svg.attr("width"),
  height = +svg.attr("height"),
  nodeLayer = svg.append('g'),
  node;

var list = [];
var links = [];

var simulation = d3.forceSimulation(list)
  .force("charge", d3.forceManyBody().strength(-1000))
  .force("link", d3.forceLink(links).distance(200))
  .force("center", d3.forceCenter(width / 2, height / 2))
  .on("tick", ticked);

function createNodes(index) {
  links.push({
    'source': 0,
    'target': index
  })
  list.push({
    "id": index,
    "name": "server " + index
  });
  return list;
}

var iteration = 0;
update(createNodes(iteration)); // just simulating updates 

d3.interval(function(timer) {
  iteration++;
  update(createNodes(iteration));
}, 1000); //<-- this was commented out incorrectly just now

function update(items) {
  var dataJoin = nodeLayer.selectAll(".node")
    .data(items, function(d) {
      return d.id;
    });
  node = dataJoin.enter() // insert
    .append("g")
    .attr("class", "node");

  node.append("circle") // insert
    .attr("r", 2.5)
    .attr('class', 'circle')
    .merge(dataJoin) // not the class, the actual selected group you called enter() on
    .select('.circle')
    .style('fill', 'red') // just for testing purposes
    .exit().remove(); // exit

  node.append("text") // insert
    .attr("dy", 3)
    .attr('class', 'text')
    .text(function(d) {
      return d.name;
    })
    .merge(dataJoin)
    .select('.text')
    .style('fill', 'green') // fill is a style

  dataJoin.exit().remove();
  simulation.nodes(list);
  simulation.force("link").links(links);
  simulation.alpha(1).restart();
}

function ticked() {
  node.attr("transform", function(d) {
    var a = d.x || 0;
    var b = d.y || 0;
    return "translate(" + a + ", " + b + ")";
  });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.4.1/d3.min.js"></script>
<svg height="300" width="300"></svg>

there are bunch of bugs,

first of all 'update'(misnomer) should be called with the list of all the data nodes not just the change.

you might want to explore https://bl.ocks.org/ and copy how people are doing force directed graphs. you need links to have forces.

the idea behind merge is to compare the new list with the old list, which means you need to use dataJoin or the group that has the data/id attached.

I am not an expert, do take a look at all the examples of force-directed graphs and see how they update/merge. (there's more than 1 way to update/restart the graph)


Related Query

More Query from same tag