javascript - exit().remove() 在我的 D3.js 强制布局图中不起作用

标签 javascript d3.js

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

该图非常基本 - 它以城市为节点,连接到代表其所在国家的节点。为简单起见,我只创建了六个节点。

D3JS Graph

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

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

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

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

代码

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

最佳答案

在 D3 中,更改数据不会自动更改 SVG(或 Canvas 、HTML...)元素。您必须“重新绘制”您的数据可视化。

好消息是您拥有(几乎)所有选择。因此,为了向您展示总体思路,我将所有渲染代码放在 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>

当然,正如我所说,这只是为了给您提供总体思路:例如,单击不会删除链接。但现在您知道如何让它发挥作用了。

关于javascript - exit().remove() 在我的 D3.js 强制布局图中不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41365884/

相关文章:

javascript - Service Worker 从缓存中获取请求,因此页面不会从新数据中更新

javascript - 如何显示 HTTP 401 基本身份验证对话框

d3.js - 在 D3 中变换转换期间更新圆的位置

python - Django Rest Framework 嵌套序列化器

javascript - D3js 将路径 `d` 属性绑定(bind)到数据

javascript - Sublime Text 3 PHP 语法不高亮显示

javascript - 我怎样才能使 AngularJS 指令停止传播?

javascript - 将 json 中的文本分成多行以在 D3 强制布局中显示标签

javascript - Bootstrap 日期选择器在选择日期后不会自动关闭

javascript - 将保存 CSV 数据的数组合并到一个对象数组中