Home > Blockchain >  D3 outdated links still visible
D3 outdated links still visible

Time:09-22

What I got:

I got a D3 forced graph, with two different link types. I do visualize those two types differently. need as a simple line and uses dashed. To do so I got two different CSS classes for the links and simply switch the type if clicked. To finally visualize this changes I call the main initialize() function again.

Whats the problem:

As soon as I click on one of those links and switch the type, the outdated lines are still visible. I miss the point how to avoid such behavior? How can I make sure, that the outdated lines are gone? I appreciate any hint.

Update:

I added svg.selectAll("line").remove() before the re-init. But I doubt its best practice, further sometimes the lines just disappear completely.

 var graph = {
            "nodes": [
                {
                    "id": 1
                },
                {
                    "id": 2
                },
                {
                    "id": 3
                }
            ],
            "links": [
                {
                    "source": 1,
                    "target": 2,
                    "type": "uses"
                },
                {
                    "source": 2,
                    "target": 3,
                    "type": "needs"
                },
                {
                    "source": 3,
                    "target": 1,
                    "type": "needs"
                }

            ]
        }

        var svg = d3.select("svg")
            .attr("width", window.innerWidth)
            .attr("height", window.innerHeight)

        var force = d3.forceSimulation()
            .force("link", d3.forceLink().id(function (d) {
                return d.id
            }).distance(80))
            .force("charge", d3.forceManyBody().strength(-100))
            .force("center", d3.forceCenter(window.innerWidth / 2, window.innerHeight / 2))
            .force("collision", d3.forceCollide().radius(90))

        initialize()

        function initialize() {

            link = svg.selectAll(".link")
                .data(graph.links)
                .join("line")
                //.attr("class", "link")
                .attr("class", function (d) {
                    if (d.type === "uses") {
                        return "uses"
                    } else {
                        return "needs"
                    }
                })
                .on("dblclick", function (event, d) {
                    if (d.type === "uses") {
                        d.type = "needs"
                    } else if (d.type === "needs") {
                        d.type = "uses"
                    }
                    svg.selectAll("line").remove()
                    initialize()
                })
    
            node = svg.selectAll(".node")
                .data(graph.nodes, d => d.id)
                .join("g")
                .attr("class", "node")
                .call(d3.drag()
                    .on("start", dragStarted)
                    .on("drag", dragged)
                    .on("end", dragEnded)
                )

            node.selectAll("circle")
                .data(graph.nodes)
                .join("circle")
                .attr("r", 30)
                .style("fill", "whitesmoke")

            force
                .nodes(graph.nodes)
                .on("tick", ticked);

            force
                .force("link")
                .links(graph.links)
        }

        function ticked() {
            // update link positions
            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;
                });

            // update node positions
            node
                .attr("transform", function (d) {
                    return "translate("   d.x   ", "   d.y   ")";
                });
        }

        function dragStarted(event, d) {
            if (!event.active) force.alphaTarget(0.3).restart();
            d.fx = d.x;
            d.fy = d.y;

            PosX = d.x
            PosY = d.y
        }

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

        function dragEnded(event, d) {
            if (!event.active) force.alphaTarget(0);
            d.fx = undefined;
            d.fy = undefined;
        }
  body {
        height: 100%;
        background: #e6e7ee;
        overflow: hidden;
        margin: 0px;
    }

    line {
        stroke-width: 6px;
    }
    
    line.uses {
        stroke: grey;
        stroke-dasharray: 5;
    }

    line.needs {
        stroke: black;
    }

    line:hover {
        stroke: goldenrod;
    }
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- d3.js framework -->
    <script src="https://d3js.org/d3.v6.js"></script>
    
</head>

<body>
    <svg id="svg"></svg>
</body>

</html>

CodePudding user response:

In the segment:

  link = svg.selectAll(".link")
            .data(graph.links)
            .join("line")
            //.attr("class", "link")
            .attr("class", function (d) {
                if (d.type === "uses") {
                    return "uses"
                } else {
                    return "needs"
                }
            })

The selectin is selecting the link class, but the lines actually have uses or needs. You can instead select the previous uses and needs:

link = svg.selectAll("line.uses,line.needs")

This will make the .join() remove the unused lines of those classes.

  • Related