I'm having difficulties updating my D3 force network. The goal is to have the possibility to redraw the graph with filtered data. For simplicity I provided the code example below with two Dummydatasets (complete and filtered) which can be chosen with buttons. But instead of redrawing the graph it just keeps appending new svg-Elements besides the already existing. I guess it has something to do with calling the entire showData function again and again. Though I tried different solutions it keeps appending the svgs besides or the g-elements on top of each other. Here's the code and the data:
index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script src="network.js"></script>
<script src="getData.js"></script>
</head>
<body>
<button onclick="loadData('dummyData1.json')">Data1</button>
<button onclick="loadData('dummyData2.json')">Data2</button>
<div id="network"></div>
</body>
</html>
getData.js
let store = {}
function loadData(url) {
console.log(url)
return Promise.all([
d3.json(url)
]).then(datasets => {
store.graph = datasets[0];
showData(store.graph)
return store;
})
}
network.js
function showData (data){
var toggle = 0;
width=500;
height=500;
var linkedByIndex = {};
data.links.forEach(function(d) {
linkedByIndex[d.source + ',' + d.target] = 1;
linkedByIndex[d.target + ',' + d.source] = 1;
});
let simulation = d3.forceSimulation()
.force("link", d3.forceLink().id((d) => d.id).distance(10))
.force("charge", d3.forceManyBody().strength(d => -20))
.force("center", d3.forceCenter(width / 2, height / 2))
var color = d3.scaleOrdinal()
.domain(data.nodes)
.range(['#e41a1c','#377eb8','#4daf4a','#984ea3','#ff7f00','#ffff33','#a65628'])
svg = d3.select("#network").append("svg")
.attr("width", width)
.attr("height", height)
var g = svg.append("g").attr("class", "everything")
var link = g.append("g")
.selectAll(".link")
var node = g.append("g")
.selectAll(".node")
var drag_handler = d3.drag()
.on("start", drag_start)
.on("drag", drag_drag)
.on("end", drag_end);
drag_handler(node);
var zoom_handler = d3.zoom()
.on("zoom", zoom_actions);
zoom_handler(svg);
update();
/** Functions **/
function update() {
node = node.data(data.nodes)
node.exit().remove()
var newNode = node.enter().append("circle")
.attr('class', 'node')
.attr("r", 5)
.attr('fill', function(d) {return(color(d.country))})
.on('click', function(d, i) {
if (toggle == 0) {
// Ternary operator restyles links and nodes if they are adjacent.
d3.selectAll('.link').style('stroke-opacity', function (l) {
return l.target == i || l.source == i ? 1 : 0.1;
});
d3.selectAll('.node').style('opacity', function (n) {
return neighboring(n, i) ? 1 : 0.1;
});
d3.select(this).style('opacity', 1);
toggle = 1;
}
else {
// Restore nodes and links to normal opacity.
d3.selectAll('.link').style('stroke-opacity', '0.3');
d3.selectAll('.node').style('opacity', '1');
toggle = 0;
}
})
node = node.merge(newNode)
link = link.data(data.links)
link.exit().remove();
var newLink = link.enter().append("line")
.attr('class', 'link')
.attr("stroke", "black")
.attr("stroke-opacity", 0.3)
link = link.merge(newLink)
simulation
.nodes(data.nodes)
.on("tick", tickActions);
simulation.force("link")
.links(data.links);
simulation.alpha(1).alphaTarget(0).restart();
}
function drag_start(event, d) {
if (!event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function drag_drag(event, d) {
d.fx = event.x;
d.fy = event.y;
}
function drag_end(event, d) {
if (!event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
function neighboring(a, b) {
return linkedByIndex[a.id + ',' + b.id];
}
//Zoom functions
function zoom_actions(event, d){
g.attr("transform", event.transform)
}
function tickActions() {
node
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
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; });
}
}
dummyData1.json
{"nodes": [
{"id":1, "country":"Germany"},
{"id":2, "country":"UK"},
{"id":3, "country":"USA"},
{"id":4, "country":"China"},
{"id":5, "country":"Greece"},
{"id":6, "country":"Brazil"}
],
"links": [
{"source": 2, "target": 4},
{"source": 1, "target": 4},
{"source": 3, "target": 5},
{"source": 6, "target": 1},
{"source": 5, "target": 4},
{"source": 1, "target": 2},
{"source": 2, "target": 3}
]}
dummyData2.json
{"nodes": [
{"id":1, "country":"Germany"},
{"id":2, "country":"UK"},
{"id":3, "country":"USA"},
{"id":4, "country":"China"}
],
"links": [
{"source": 2, "target": 4},
{"source": 1, "target": 4},
{"source": 1, "target": 2},
{"source": 2, "target": 3}
]}
Thanks in advance:-)