exit()。remove()在我的D3.js Force Layout Graph中不起作用

时间:2016-12-28 16:18:18

标签: javascript d3.js

我创建了一个简单的D3 Force Layout图。 Please check it out in the JSFiddle here.

图表非常基础 - 它将城市作为节点连接到表示他们所在的节点的节点。为简单起见,我只制作了六个节点。

D3JS Graph

我创建了一个名为deleteNodeOnClick()的函数,并将其设置在nodes上,就像这样

 var nodeEnter = node.enter()
        .append('g')
        .attr('class', 'node')
        .on("click", deleteNodeOnClick)

当您点击图表中的某个节点时,该节点将从数据中删除(实际上为了简单起见,第一个节点暂时从数据中删除)但是它不会从可视图中删除。您可以查看控制台,看看它实际上是从数据中删除的。

为什么不呢?我完全被难过了。

代码

var data = {
  nodes: [{
    name: "Canada"
  }, {
    name: "Montreal"
  }, {
    name: "Toronto"
  }, {
    name: "USA"
  }, {
    name: "New York"
  }, {
    name: "Los Angeles"
  }],
  links: [{
    source: 0,
    target: 1
  }, {
    source: 0,
    target: 2
  }, {
    source: 3,
    target: 4
  }, {
    source: 3,
    target: 5
  }, ]
};

var node;
var link;
var force;
var width = 400,
  height = 400;

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

force = d3.layout.force()
  .size([width, length])
  .nodes(data.nodes)
  .links(data.links)
  .gravity(.1)
  .alpha(0.01)
  .charge(-400)
  .friction(0.5)
  .linkDistance(100)
  .on('tick', forceLayoutTick);

var link = svg.selectAll(".link")
  .data(data.links);
var linkEnter = link.enter()
  .append('line')
  .attr('class', 'link');
link.exit().remove();

node = svg.selectAll('.node')
        .data(data.nodes, function(d){
            return d.name;
        });

    node.exit().remove();

    var nodeEnter = node.enter()
                        .append('g')
                        .attr('class', 'node')
                        .on("click", deleteNodeOnClick)
                        //.attr('r', 8)
                        //.attr('cx', function(d, i){ return (i+1)*(width/4); })
                        //.attr('cy', function(d, i){ return height/2; })
                        .call(force.drag);

  nodeEnter
        .append("circle")
        .attr("cx", 0)
        .attr("cy", 0)
        .attr("r", 10)
        .style("fill", "purple");

    nodeEnter
        .append("text")
        .text(function(d) { return d.name })
        .attr("class", "label")
        .attr("dx", 0)
        .attr("dy", ".35em");

 force.start();


 function forceLayoutTick(){

            node.attr("transform", function(d) {

                // Keep in bounding box
                d.x = Math.max(10, Math.min(width - 10, d.x)); 
                d.y = Math.max(10, Math.min(height - 10, d.y));

                return "translate(" + d.x + "," + 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; });
    };

    function deleteNodeOnClick(d){
        var dataBefore = JSON.parse(JSON.stringify(data.nodes));
        // Just delete the first node, for demonstration purposes
      data.nodes.splice(0, 1);
      console.info("Node should be removed", dataBefore, data.nodes);
    }

CSS

#graph {
  width: 100%;
  height: 100%;
}

#graph svg {
  background-color: #CCC;
}

.link {
  stroke-width: 2px;
  stroke: black;
}

.node {
  background-color: darkslategray;
  stroke: #138;
  width: 10px;
  height: 10px;
  stroke-width: 1.5px;
}

.node text {
  pointer-events: none;
  font: 10px sans-serif;
}

.label {
  display: block;
}

2 个答案:

答案 0 :(得分:3)

在D3中,更改数据不会自动更改SVG(或画布或HTML ...)元素。你必须“重新绘制”你的dataviz。

好消息是你(几乎)有所有的选择。因此,为了向您展示一般概念,我将所有渲染代码放在draw函数中,该函数在点击时调用:

function deleteNodeOnClick(d){
    data.nodes = data.nodes.filter(function(e){
        return e.name !== d.name;
    });
    draw();
}

查看演示:

var data = {
    nodes: [{
        name: "Canada"
    }, {
        name: "Montreal"
    }, {
        name: "Toronto"
    }, {
        name: "USA"
    }, {
        name: "New York"
    }, {
        name: "Los Angeles"
    }],
    links: [{
        source: 0,
        target: 1
    }, {
        source: 0,
        target: 2
    }, {
        source: 3,
        target: 4
    }, {
        source: 3,
        target: 5
    }, ]
};

var node;
var link;
var force;
var width = 400,
    height = 400;

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);



draw();

function draw() {

    force = d3.layout.force()
        .size([width, height])
        .nodes(data.nodes)
        .links(data.links)
        .alpha(0.01)
        .charge(-400)
        .friction(0.5)
        .linkDistance(100)
        .on('tick', forceLayoutTick);

    var link = svg.selectAll(".link")
        .data(data.links);
    var linkEnter = link.enter()
        .append('line')
        .attr('class', 'link');
    link.exit().remove();

    node = svg.selectAll('.node')
        .data(data.nodes, function(d) {
            return d.name;
        });

    node.exit().remove();

    var nodeEnter = node.enter()
        .append('g')
        .attr('class', 'node')
        .on("click", deleteNodeOnClick)
        //.attr('r', 8)
        //.attr('cx', function(d, i){ return (i+1)*(width/4); })
        //.attr('cy', function(d, i){ return height/2; })
        .call(force.drag);

    nodeEnter
        .append("circle")
        .attr("cx", 0)
        .attr("cy", 0)
        .attr("r", 10)
        .style("fill", "purple");

    nodeEnter
        .append("text")
        .text(function(d) {
            return d.name
        })
        .attr("class", "label")
        .attr("dx", 0)
        .attr("dy", ".35em");

    force.start();


    function forceLayoutTick() {

        node.attr("transform", function(d) {

            // Keep in bounding box
            d.x = Math.max(10, Math.min(width - 10, d.x));
            d.y = Math.max(10, Math.min(height - 10, d.y));

            return "translate(" + d.x + "," + 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;
        });
    };

};

function deleteNodeOnClick(d) {
    data.nodes = data.nodes.filter(function(e) {
        return e.name !== d.name;
    });
    draw();
}
#graph svg {
  background-color: #CCC;
}

.link {
  stroke-width: 2px;
  stroke: black;
}

.node {
  background-color: darkslategray;
  stroke: #138;
  width: 10px;
  height: 10px;
  stroke-width: 1.5px;
}

.node text {
  pointer-events: none;
  font: 10px sans-serif;
}

.label {
  display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<body></body>

当然,正如我所说,这只是为了给你一般的想法:例如,点击不会删除链接。但现在你知道如何把它付诸实践。

答案 1 :(得分:0)

你的功能对图表没有任何作用:(只是数据)

function deleteNodeOnClick(d){
    var dataBefore = JSON.parse(JSON.stringify(data.nodes));
    // Just delete the first node, for demonstration purposes
    data.nodes.splice(0, 1);
    console.info("Node should be removed", dataBefore, data.nodes);
}

从图表中重新渲染或删除...

示例,只是删除(特定)“点”

function deleteNodeOnClick(d) {
  var theElement = node.filter(function(da, i) {
      if (i === d.index) {
        console.log(da);
        return true;
      }
    })
  console.dir(theElement);
  theElement.remove();
  var dataBefore = JSON.parse(JSON.stringify(data.nodes));
  // Just delete the first node, for demonstration purposes
  data.nodes.splice(d.index, 1);
  console.info("Node should be removed", dataBefore, data.nodes);
}

请注意,不要删除连接OTHER节点的“line”,我将把这项练习留给你做。

更紧凑的版本,没有所有日志记录等。

function deleteNodeOnClick(d) {
   d3.select(node[0][d.index]).remove();
   data.nodes.splice(d.index, 1);
 }

(为访问本网站的其他人添加) 对于d3版本4,这将是

function deleteNodeOnClick(d) {
   d3.select(node._groups[0][d.index]).remove();
   data.nodes.splice(d.index, 1);
 }