duckduck
duckduck

Reputation: 33

D3.js update leaves duplicate svg elements

I am using the D3.js force layout to create a graph. To update nodes and edges I follow the example code at http://bl.ocks.org/mbostock/1095795. This means I have this code to initialize the layout:

var nodes   = [];
var edges   = [];
var force = d3.layout
                 .force()
                 .nodes(nodes)
                 .links(edges)
                 ... 
var link = svg.selectAll('.link');
var node = svg.selectAll('.node');

And I update the data in a function:

//(nodes and edges array has been updated before)
link = link.data(force.links());
link.enter()
    .append('g')
    .attr('class', 'link');
link.append('line')
    .style('stroke-width', function (d) {
        return d.bond*2 + 'px';
    });
link.exit().remove();

node = node.data(force.nodes());
node.enter()
    .append('g')
    .attr('class', 'node')
    .call(force.drag);

node.append('circle')
    .attr('r', function (d) { return radius(d.size) })
    .style('fill', function (d) { return color(d.type) });
node.exit().remove();

force.start();

I inspected the content of the nodes and edges arrays and the force.nodes() and force.links() and it seems like everything is correct, but the graph shows edges of previous data. So I also looked at the svg and it seems like the group (<g>) of nodes and edges contains more than one 'line' and 'circle' element respectively:

<g class="link">
    <line x1="39.28621930166058" y1="256.22690655336356"
    x2="217.9040028325144" y2="12.304247302113906" 
    style="stroke-width: 2px;"></line>

    <line x1="199.64928632623412" y1="275.35140495955585"
    x2="275.5229250036658" y2="100.33170397523202"
    style="stroke-width: 2px;"></line>
</g> 

The same applies to the nodes.

How can I remove the previous elements from the group when updating? I can't seem to find the answer in the linked example or in the d3 docs.

Upvotes: 2

Views: 1512

Answers (1)

mgraham
mgraham

Reputation: 6207

link.enter()
    .append('g')
    .attr('class', 'link');
link.append('line')
    .style('stroke-width', function (d) {
        return d.bond*2 + 'px';
    });
link.exit().remove();

link is the selection/data join of g.link elements with your data.

Looking at the above you append a new g element for each new link via .enter(), but then you append a new line element to the new and existing links because there's no enter() qualifier there.

Try

var newLinks = link.enter()
    .append('g')
    .attr('class', 'link');
newLinks.append('line')
    .style('stroke-width', function (d) {
        return d.bond*2 + 'px';
    });

PS. If that works for the lines then it's the same situation for the nodes.

Upvotes: 2

Related Questions