D3 Appending Duplicates

by k_sav

HTML

<!DOCTYPE html>
<html>

  <head>
    <meta charset="utf-8">
    <script src="https://d3js.org/d3.v5.min.js"></script>

  </head>

  <body>
  </body>

</html>
https://jsfiddle.net/AlexMarshaall/srL3huk9/4/#

CSS

svg {
  background-color: #FFF;
  cursor: default;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  -o-user-select: none;
  user-select: none;
}

svg:not(.active):not(.ctrl) {
  cursor: crosshair;
}

path.link {
  fill: none;
  stroke: #000;
  stroke-width: 4px;
  cursor: default;
}

svg:not(.active):not(.ctrl) path.link {
  cursor: pointer;
}

path.link.selected {
  stroke-dasharray: 10, 2;
}

path.link.dragline {
  pointer-events: none;
}

path.link.hidden {
  stroke-width: 0;
}

circle.node {
  stroke-width: 1.5px;
  cursor: pointer;
}

text {
  font: 12px sans-serif;
  pointer-events: none;
}

text.id {
  text-anchor: middle;
  font-weight: bold;
}

JavaScript

const svg = d3.select('body')
  .append('svg')
  .attr('width', 900)
  .attr('height', 500);

let hoverOverNode = null; // the node the mouse is currently hovering over
let mousedownNode = null; // the node that the mouse went down over

function resetMouseVars() {
  mousedownNode = null;
}

var lastNodeId = 1;
var node1 = {
  id: "n" + lastNodeId++,
  xVal: 50,
  yVal: 50
};

var node2 = {
  id: "n" + lastNodeId++,
  xVal: 100,
  yVal: 100
}

const nodesData = [node1, node2];

const edgeData = [{
  source: node1,
  target: node2
}];

let paths = svg.append('svg:g').selectAll('path');
let nodes = svg.append('svg:g').selectAll('g');

const dragLine = svg.append('svg:path')
  .attr('class', 'link dragline hidden')
  .attr('d', 'M0,0L0,0');


function restart() {

  nodes = nodes.data(nodesData, (d) => d.id); // Nodes is just the nodes to update

  nodes.selectAll('g')
    .style('fill', "DarkGreen");

  nodes.exit().remove();

  var newNodesToBeAdded = nodes.enter().append('svg:g');

  newNodesToBeAdded.attr('transform', (d) => `translate(${d.xVal},${d.yVal})`)
    .attr('id', (d) => d.id);

  newNodesToBeAdded.append('svg:circle')
    .attr('class', 'node')
    .attr('r', 12)
    .attr('stroke-width', 3)
    .attr('stroke', 'black')
    .style('fill', "DarkGreen")
    .on('mouseover', function(d) {
      hoverOverNode = d;
      d3.select(this).attr('transform', 'scale(1.1)');
    })
    .on('mouseout', function(d) {
      hoverOverNode = null;
      d3.select(this).attr('transform', '');
    })
    .on('mousedown', (d) => {
      if (d3.event.ctrlKey) return;
      mousedownNode = d;
      dragLine
        .classed('hidden', false)
        .attr('d', `M${mousedownNode.xVal},${mousedownNode.yVal}L${mousedownNode.xVal},${mousedownNode.yVal}`);
      restart();
    })
    .on('mouseup', (d) => {
      dragLine.classed('hidden', true);
    });

  newNodesToBeAdded.append('svg:text')
    .attr('x', 0)
    .attr('y', 4)
    .attr('class', 'id')
    .text((d) =>...