'D3 V5 Force layout cannot properly display nodes, links and labels

This used to work with new set of data but only by redrawing the entire svg with an entire new dataset. Now I changed it to be able to add one node & link at a time but obviously made a mess. I added an ADD NODE link but won't render nodes properly. I've seen other solutions but it doesn't seem to work for me or I am just not applying it properly. I do see via the console that I am getting X and Y, vx and vy values I didn't have before so, one thing down. Now I just need to render it properly. I am coming back from years of not using D3 and seem I can't seem to understand what is going on. Any help, pointers or guidance would be appreciated. I should be able to take care of the REMOVE NODE option once ADD NODE is fixed.

Thank you in advance, Me

Attached is the jfiddle.

<div style="text-align: right">
    <a style="padding-right:20px; color:green" onclick=addNodeGroup()>[ADD NODE]</a>

    <a style="padding-right:20px; color:red" onclick=nothingYet()>[REMOVE NODE]</a>
</div>

<style>
.svg-container {
    display: inline-block;
    position: relative;
    width: 100%;
    padding-bottom: 100%; /* aspect ratio */
    vertical-align: top;
    overflow: hidden;
}
.svg-content-responsive {
    display: inline-block;
    position: absolute;
    top: 10px;
    left: 0;
}
div.tooltip {
  position: absolute;
  text-align: center;
  width: 140px;
  height: 50px;
  padding: 2px;
  font: 12px sans-serif;
  background: lightgreen;
  border: 0px;
  border-radius: 8px;
  pointer-events: none;
}
</style>
<div id="clusterGraph"></div>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script src='https://d3js.org/d3.v5.min.js'></script>

<script>
var width = $(document).width();
var height = $(document).height();
var color = d3.scaleOrdinal(d3.schemeCategory10);
var graphData = {
"hosts": [{"id": "HELLO", "group": 12345},{"id": '555', "group": 555}],
"hosts_links": [{"source": '555', "target": "HELLO", "value": 5}]
}
var label_list = {'hosts': [],'hosts_links': []};
var adjlist = [];
var labelLayout;
var graphLayout;

var svg = d3.select("div#clusterGraph")
    .append("div")
    .classed("svg-container", true) //container class to make it responsive
    .append("svg")
    .attr("viewBox","0 0 " + width + " " + height)
    .classed("svg-content-responsive", true);

svg.call(
        d3.zoom()
            .scaleExtent([.1, 4])
            .on("zoom", function() { container.attr("transform", d3.event.transform); })
    );

var container = svg.append("g");
var div = d3.select("div").append("div")
    .attr("class", "tooltip")
    .style("opacity", 0);

var linkgroup = container.append("g").attr("class", "links")

var nodegroup = container.append("g").attr("class", "nodes")

var labelNodegroup = container.append("g").attr("class", "labelNodes")

function groups(g){
    linkgroup.selectAll("line")
        .data(g.hosts_links)
        .enter()
        .append("line")
        .attr("stroke", "#9e9d9d")
        .attr("stroke-width", "1px");

    nodegroup.selectAll("g")
        .data(g.hosts)
        .enter()
        .append("circle")
        .attr("r", 5)
        .attr("fill", function (d) {return color(d.group);});

    labelNodegroup.selectAll("text")
        .data(label_list.hosts)
        .enter()
        .append("text")
        .text(function(d, i) { return i % 2 == 0 ? "" : g.hosts.id; })
        .style("fill", "#9e9d9d")
        .style("font-family", "Arial")
        .style("font-size", 12)
        .style("pointer-events", "none"); // to prevent mouseover/drag capture

    linkgroup.exit().remove();
    nodegroup.exit().remove();
    labelNodegroup.exit().remove();

    nodegroup.call(updateNode)

    nodegroup.on("mouseover", focus).on("mouseout", unfocus);

    nodegroup.call(
            d3.drag()
                .on("start", dragstarted)
                .on("drag", dragged)
                .on("end", dragended));
}

function append_to_graphData(g) {
    g.hosts.forEach(function (d, i) {
        graphData.hosts.push({hosts: d});
        label_list.hosts.push({hosts: d});
        label_list.hosts.push({hosts: d});
        label_list.hosts_links.push({
            source: i * 2,
            target: i * 2 + 1
        });
    });
    g.hosts_links.forEach(function (d) {
        graphData.hosts_links.push({hosts_links: d});
        adjlist[d.source.index + "-" + d.target.index] = true;
        adjlist[d.target.index + "-" + d.source.index] = true;
    });
    console.log(label_list)
    return graphData
}

function layouts(g) {
    labelLayout = d3.forceSimulation(label_list.hosts)
        .force("charge", d3.forceManyBody().strength(-50))
        .force("link", d3.forceLink(label_list.hosts_links).distance(0).strength(2));

    graphLayout = d3.forceSimulation(g.hosts)
        .force("charge", d3.forceManyBody().strength(-3000))
        .force("center", d3.forceCenter(width / 2, height / 2))
        .force("x", d3.forceX(width / 2).strength(1))
        .force("y", d3.forceY(height / 2).strength(1))
        .force("link", d3.forceLink(g.hosts_links).id(function (d) {
            return d.id;
        }).distance(50).strength(1))
        .on("tick", ticked);
}

function neigh(a, b) {
    return a == b || adjlist[a + "-" + b];
}

function ticked() {
    nodegroup.call(updateNode);
    linkgroup.call(updateLink);

    labelLayout.alphaTarget(0.3).restart();
    labelNodegroup.each(function(d, i) {
        //console.log(labelNode)
        if(i % 2 == 0) {
            d.x = d.hosts.x;
            d.y = d.hosts.y;
        } else {
            var b = this.getBBox();

            var diffX = d.x - d.hosts.x;
            var diffY = d.y - d.hosts.y;

            var dist = Math.sqrt(diffX * diffX + diffY * diffY);

            var shiftX = b.width * (diffX - dist) / (dist * 2);
            shiftX = Math.max(-b.width, Math.min(0, shiftX));
            var shiftY = 16;
            this.setAttribute("transform", "translate(" + shiftX + "," + shiftY + ")");
        }
    });
    labelNodegroup.call(updateNode);
}

function fixna(x) {
    if (isFinite(x)) return x;
    return 0;
}

function focus(d) {
    var index = d3.select(d3.event.target).datum().index;
    nodegroup.style("opacity", function(o) {
        return neigh(index, o.index) ? 1 : 0.1;
    });
    labelNodegroup.attr("display", function(o) {
      return neigh(index, o.hosts.index) ? "block": "none";
    });

    div.style("opacity", .9);
    div.html("Some 1: " + d3.event.pageX
      + "</br>" + "Some 2: " + d3.event.pageX
      + "</br>" + "Some 3: " + d3.event.pageX)
      .style("left", (d3.event.pageX) + 10 + "px")
      .style("top", (d3.event.pageY + 10) + "px");

    linkgroup.style("opacity", function(o) {
        return o.source.index === index || o.target.index === index ? 1 : 0.1;
    });
}

function unfocus() {
   div.style("opacity", 0);
   labelNodegroup.attr("display", "block");
   nodegroup.style("opacity", 1);
   linkgroup.style("opacity", 1);
}

function updateLink(link) {
    link.attr("x1", function(d) { return fixna(d.source.x); })
        .attr("y1", function(d) { return fixna(d.source.y); })
        .attr("x2", function(d) { return fixna(d.target.x); })
        .attr("y2", function(d) { return fixna(d.target.y); });
}

function updateNode(node) {
    node.attr("transform", function(d) {
        return "translate(" + fixna(d.x) + "," + fixna(d.y) + ")";
    });
}

function dragstarted(d) {
    d3.event.sourceEvent.stopPropagation();
    if (!d3.event.active) graphLayout.alphaTarget(0.3).restart();
    d.fx = d.x;
    d.fy = d.y;
}

function dragged(d) {
    d.fx = d3.event.x;
    d.fy = d3.event.y;
}

function dragended(d) {
    if (!d3.event.active) graphLayout.alphaTarget(0);
    d.fx = null;
    d.fy = null;
}

function addNodeGroup(){
    new_node_data = {
        "hosts":
            [{"id": "ABC-100", "group": 75},{"id": "555", "group": 75}], //, "index": graphData.hosts.length, "x": 450, "y": 380, "vx": 0, "vy": 0}],
        "hosts_links":
            [{'source': '555', 'target': 'ABC-100', 'value': 6}]//, 'index': graphData.hosts_links.length, 'value': 6}]
    }

    var mynewdata = append_to_graphData(new_node_data)
    layouts(mynewdata)
    groups(mynewdata)

    graphLayout.nodes(graphData.hosts)
    graphLayout.force("link").links(graphData.hosts_links)
    graphLayout.alpha(.5).restart()
}
</script>

https://jsfiddle.net/BBQBeginner/s9fchka6/2/#&togetherjs=HSVdChE3Lh



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source