D3 mousedown event deleting the wrong node

前端 未结 2 956
庸人自扰
庸人自扰 2021-01-25 10:28

I am trying to add a delete node feature in this jsfiddle

The refresh method is adding on(\"mousedown\", mousedownNode) event to each circle, b

相关标签:
2条回答
  • 2021-01-25 11:05

    As you said, the correct nodes are getting deleted and the data bound to the <g class="node"></g> also seems correct BUT you're not UPDATING the node texts.

    This is what you have:

    nodeEnter
     .append("text")
     .attr("dx", 12)
     .attr("dy", ".35em")
     .text(function(d) {
      return d.id;
     });
    

    This is what I've changed it to:

    nodeEnter
     .append("text")
     .attr("dx", 12)
     .attr("dy", ".35em");
    
    node.select('text')
     .text(function(d) {
      return d.id;
     });
    

    You can also change this by assigning the nodeText to a variable like this var nodeText = ...

    But here's the point, when you call the refresh after say deleting a node, the exit() selection works BUT the existing nodes DO NOT update by themselves. Hope this makes sense. Read How Selections Work

    JSFIDDLE and here's the snippet:

    EDIT: Removing a draggable SVG element results in an error. To fix, change the exit selection line to:

    node.exit().on('mousedown.drag', null).remove();
    

    that removes the drag event bound before removing the element.

    rect {
      fill: none;
      pointer-events: all;
    }
    
    .node {
      fill: #000;
    }
    
    .cursor {
      fill: green;
      stroke: brown;
      pointer-events: none;
    }
    
    .link {
      stroke: #999;
    }
    
    .node text {
      pointer-events: none;
      font: 10px sans-serif;
    }
    
    path.link {
      fill: none;
      stroke: #666;
      stroke-width: 1.5px;
    }
    <script src="https://d3js.org/d3.v3.min.js"></script>
            <button id="ref" onclick="refresh()">refresh </button>
        <script>
        //setInterval(refresh, 15000);
    
        function addNodeCanvas(nodeName,g) {
            var node = {        x: 900,    y: 900,        id: nodeName,    grp:g  };
            var n = nodes.push(node);
            console.log(node);
            refresh();
        }
        
    
        function addLinkCanvas(idSrc, idTarget) {
            if (idSrc != idTarget) {
                var s = {},        t = {};
                nodes.forEach(function(curNode) {
                    if (typeof curNode.id != "undefined") {
                        if (curNode.id == idSrc) {          s = curNode;        }
                        if (curNode.id == idTarget) {          t = curNode;        }
                    }
                });
    
                //console.log( { s,t});
                links.push({     source: s,      target: t    });
            };
            refresh();
        }
    
    
    
    var fill = d3.scale.category20();
    
    
    var links = [{ source:  "FH", target: "TP" }];
    var nodes = [
        { id: "FH", x: 100, y: 110 },
        { id: "TP", x: 200, y: 110 },
            { id: "GW1", x: 200, y: 110 },
        { id: "GW", x: 200, y: 110 },
            { id: "GW2", x: 200, y: 110 },
        { id: "DB", x: 100, y: 110 }
      ]
    
    var width = 600,
      height = 400,
        radius = 8;
    
    
    var map = {}
    nodes.forEach(function(d,i){
      map[d.id] = i;
    })
    
    links.forEach(function(d) {
      d.source = map[d.source];
      d.target = map[d.target];
    })
    
    var force = d3.layout
      .force()
      .size([width, height])
      .nodes(nodes) .links(links)
      .linkDistance(50)
      .charge(-100)
      .on("tick", tick);
    
    var svg = d3
      .select("body")
      .append("svg")
      .attr("width", width)
      .attr("height", height);
    
    // build the arrow.
    var arrows = svg
      .append("svg:defs")
      .selectAll("marker")
      .data(["arrow"]) // Different link/path types can be defined here
      .enter()
      .append("svg:marker") // This section adds in the arrows
      .attr("id", String)
      .attr("viewBox", "0 -5 10 10")
      .attr("refX", 10)
      .attr("refY", -1)
      .attr("markerWidth", 4)
      .attr("markerHeight", 4)
      .attr("orient", "auto")
      .append("svg:path")
      .attr("d", "M0,-5L10,0L0,5");
    
    
    svg
      .append("rect")
      .attr("width", width)
      .attr("height", height);
    
    var nodes = force.nodes(),
      links = force.links(),
      node = svg.selectAll(".node"),
      link = svg.selectAll(".link");
    
    
    function tick() {
      link.attr("d", function(d) {
        var dx = d.target.x - d.source.x,
          dy = d.target.y - d.source.y;
        var angle = Math.atan2(dy, dx);
        var offsetX = radius * Math.cos(angle);
        var offsetY = radius * Math.sin(angle);
          dr = Math.sqrt(dx * dx + dy * dy);
        return (
          "M" +
          (d.source.x + offsetX) +
          "," +
          (d.source.y + offsetY) +
          "A" +
          dr +
          "," +
          dr +
          " 0 0,1 " +
          (d.target.x - offsetX) +
          "," +
          (d.target.y - offsetY)
        );
      });
    
      node.attr("transform", function(d) {
        return "translate(" + d.x + "," + d.y + ")";
      });
    }
    
    function mousedownNode(d, i) {
      nodes.splice(i, 1);
      links = links.filter(function(l) {
        return l.source !== d && l.target !== d;
      });
      d3.event.stopPropagation();
    
      refresh();
    }
    
    refresh();
    function refresh() {
       link = link.data(links);
      link
        .enter()
        .append("path")
        .attr("class", "link")
        .attr("marker-end", "url(#arrow)");
      link.exit().remove();
      
      node = node.data(nodes);
      var nodeEnter = node
        .enter()
        .insert("g")
        .attr("class", "node")
        .on("mousedown", mousedownNode)
        .call(force.drag);
      
      nodeEnter
        .append("image")
        .attr("xlink:href", "https://github.com/favicon.ico")
        .attr("x", -8)
        .attr("y", -8)
        .attr("width", 16)
        .attr("height", 16);
        
      nodeEnter
        .append("text")
        .attr("dx", 12)
        .attr("dy", ".35em");
        
      node.select('text')
        .text(function(d) {
          return d.id;
        });
        
      node.exit().on('mousedown.drag', null).remove();
    
       force.start();
    }
    
    
        </script>

    0 讨论(0)
  • 2021-01-25 11:24

    I didn't dig into this much, as I felt like the problem was quite obvious to me. I can't exactly explain why the GW node returns but it's something to do with your data-binding being incorrect I believe.

    When you're doing your data join you're basically telling D3 to key on an index. Now the original GW node in the DOM is going to be re-used if it weren't the last in the index because the indexes will be offset by 1.

    node = node.data(nodes);
    

    If you change this to key on a field

    node = node.data(nodes, d => d.id);
    

    You'll find your problem goes away.

    0 讨论(0)
提交回复
热议问题