D3 mousedown事件删除错误的节点

时间:2018-07-22 20:08:58

标签: javascript d3.js

我正在尝试在此jsfiddle

中添加删除节点功能

refresh方法向每个圈子添加了on("mousedown", mousedownNode)事件,但是当我单击节点GW2时,它会删除DB节点。我发现mousedownNode方法删除了正确的节点,但是刷新方法中的node = node.data(nodes);混乱了。我不确定如何解决此问题。

function mousedownNode(d, i) {
 nodes.splice(i, 1);
 links = links.filter(function(l) {
  return l.source !== d && l.target !== d;
 });
 d3.event.stopPropagation();
 refresh();
}

编辑

我有一个将节点名索引到索引映射的映射:

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];
})

2 个答案:

答案 0 :(得分:2)

正如您所说,正确的节点将被删除,并且绑定到<g class="node"></g>的数据似乎也是正确的,但是您不是更新节点文本。

这是您拥有的:

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

这就是我将其更改为:

nodeEnter
 .append("text")
 .attr("dx", 12)
 .attr("dy", ".35em");

node.select('text')
 .text(function(d) {
  return d.id;
 });

您还可以通过将nodeText分配给像这样的变量var nodeText = ...

来更改此设置

这很重要,当您说删除节点后调用刷新时,exit()选择有效,但现有节点不会自行更新。希望这是有道理的。阅读How Selections Work

JSFIDDLE和以下代码段:

编辑:删除可拖动的SVG元素会导致错误。要解决此问题,请将退出选择行更改为:

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

在删除元素之前先删除拖动事件绑定。

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>

答案 1 :(得分:0)

我没有深入研究,因为我觉得问题对我来说很明显。我无法确切说明GW节点为何返回,但是我认为这与您的数据绑定不正确有关。

进行数据连接时,基本上是在告诉D3键入索引。现在,如果它不是索引中的最后一个节点,则将重新使用DOM中原始的GW节点,因为索引将偏移1。

node = node.data(nodes);

如果您将其更改为输入字段

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

您会发现自己的问题消失了。