我正在尝试制作力图,其中有几个下拉框可以过滤显示屏上的数据。 第一个(我现在几乎就在那里)检查类型,只显示节点和具有与该类型匹配的源或目标的链接。
我现在拥有的是能够选择过滤器,图表更新,它删除不必要的节点,并重新格式化其余节点是正确的。但它只是第一次有效。如果我重新过滤,它就会开始变得混乱。
这是我的完整代码,我对javascript(& d3)非常陌生,而且我从bl.ocks.org一直无耻地窃取,所以请随时在'noob'中回答。提前谢谢。
另外,我把它放在jsfiddle上:http://jsfiddle.net/J85Vu/
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Enterprise Collaboration Map</title>
<script type="text/javascript" src="d3.v3.js"></script>
<script type="text/javascript" src="jquery-1.10.2.min.js"></script>
<style type="text/css">
path.link {
fill: none;
stroke: #666;
stroke-width: 1.5px;
}
marker#a {
fill: green;
}
path.link.a {
stroke: green;
}
circle.a {
fill: green;
stroke: #333;
stroke-width: 1.5px;
}
marker#b {
fill: blue;
}
path.link.b {
stroke: blue;
}
circle.b {
fill: blue;
stroke: #333;
stroke-width: 1.5px;
}
marker#c {
fill: orange;
}
path.link.c {
stroke: orange;
}
circle.c {
fill: orange;
stroke: #333;
stroke-width: 1.5px;
}
circle {
fill: #ccc;
stroke: #333;
stroke-width: 1.5px;
}
text {
font: 10px sans-serif;
pointer-events: none;
}
text.shadow {
stroke: #fff;
stroke-width: 3px;
stroke-opacity: .8;
}
</style>
</head>
<body>
<select class="BU">
<option value="a">A</option>
<option value="b">B</option>
<option value="c">C</option>
</select>
<script type="text/javascript">
var links = [
{source:"one",target:"two", type:"a", typeKBP:"a"},
{source:"two",target:"three", type:"a", typeKBP:"a"},
{source:"three",target:"four", type:"a", typeKBP:"a"},
{source:"four",target:"five", type:"a", typeKBP:"b"},
{source:"five",target:"six", type:"b", typeKBP:"b"},
{source:"six",target:"seven", type:"b", typeKBP:"b"},
{source:"seven",target:"eight", type:"b", typeKBP:"b"},
{source:"eight",target:"nine", type:"b", typeKBP:"c"},
{source:"nine",target:"ten", type:"c", typeKBP:"c"},
{source:"ten",target:"one", type:"c", typeKBP:"a"},
{source:"one",target:"three", type:"a", typeKBP:"a"},
{source:"two",target:"four", type:"a", typeKBP:"a"},
{source:"three",target:"five", type:"a", typeKBP:"b"},
{source:"four",target:"six", type:"a", typeKBP:"b"},
{source:"five",target:"seven", type:"b", typeKBP:"b"},
{source:"six",target:"eight", type:"b", typeKBP:"b"},
{source:"seven",target:"nine", type:"b", typeKBP:"c"},
{source:"eight",target:"ten", type:"b", typeKBP:"c"},
{source:"nine",target:"one", type:"c", typeKBP:"a"},
{source:"ten",target:"two", type:"c", typeKBP:"a"},
{source:"one",target:"four", type:"a", typeKBP:"a"},
{source:"two",target:"five", type:"a", typeKBP:"b"},
{source:"three",target:"six", type:"a", typeKBP:"b"},
{source:"four",target:"seven", type:"a", typeKBP:"b"},
{source:"five",target:"eight", type:"b", typeKBP:"b"},
{source:"six",target:"nine", type:"b", typeKBP:"c"},
{source:"seven",target:"ten", type:"b", typeKBP:"c"},
{source:"eight",target:"one", type:"b", typeKBP:"a"},
{source:"nine",target:"two", type:"c", typeKBP:"a"},
{source:"ten",target:"three", type:"c", typeKBP:"a"}
];
var inputlinks=[];
var nodes = {};
inputlinks.push(links);
// Compute the distinct nodes from the links.
links.forEach(function(link) {
link.source = nodes[link.source] || (nodes[link.source] = {name: link.source, type:link.type});
link.target = nodes[link.target] || (nodes[link.target] = {name: link.target, type:link.typeKBP});
});
var w = 1024,
h = 800;
//setup initial force layout
var force = d3.layout.force()
.gravity(0.4)
.size([w, h])
.nodes(d3.values(nodes))
.links(links)
.linkDistance(100)
.charge(-1000)
.on("tick", tick)
.start();
var svg = d3.select("body").append("svg:svg")
.attr("width", w)
.attr("height", h);
// Per-type markers, as they don't inherit styles.
svg.append("svg:defs").selectAll("marker")
.data(["a","b","c"])
.enter().append("svg:marker")
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", -1.5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5");
var path = svg.append("svg:g").selectAll("path")
.data(force.links())
.enter().append("svg:path")
.attr("class", function(d) { return "link " + d.type; })
.attr("marker-end", function(d) { return "url(#" + d.type + ")"; });
var circle = svg.append("svg:g").selectAll("circle")
.data(force.nodes())
.enter().append("svg:circle")
.attr("r", 6)
.attr("class", function(d) { return d.type; })
.call(force.drag);
var text = svg.append("svg:g").selectAll("g")
.data(force.nodes())
.enter().append("svg:g");
// A copy of the text with a thick white stroke for legibility.
text.append("svg:text")
.attr("x", 8)
.attr("y", ".31em")
.attr("class", "shadow")
.text(function(d) { return d.name; });
text.append("svg:text")
.attr("x", 8)
.attr("y", ".31em")
.attr("class","write")
.text(function(d) { return d.name; });
//jQuery update parts for drop downs.
$(document).ready(function(){
$('.BU').on('change',function(){
curBU=$('.BU').val();
//alert('The selected BU is ' + curBU);
//Filter links and rebuild nodes based on this.
minLinks={};
minLinks=links.filter(function(d){
if ((d.type==curBU) || (d.typeKBP==curBU)) {
return d
}
})
//new nodes
nodes2={};
nodes2=force.nodes().filter(function(d){return d3.keys(minLinks.filter(function(e){return e.source.name==d.name || e.target.name==d.name;})).length>0});
// minLinks.forEach(function(d) {
// d.source = nodes2[d.source] || (nodes2[d.source] = {name: d.source, type:d.type});
// d.target = nodes2[d.target] || (nodes2[d.target] = {name: d.target, type:d.typeKBP});
// });
force
.nodes(nodes2)
.links(minLinks)
.start();
//circle.remove();
newCirc=circle.data(force.nodes());
newCirc.enter().append("svg:circle")
.attr("r", 6)
.attr("class", function(d) { return d.type; })
.call(force.drag);
newCirc.exit().remove();
newPath=path.data(force.links());
newPath
.enter().append("svg:path")
.attr("class", function(d) { return "link " + d.type; })
.attr("marker-end", function(d) { return "url(#" + d.type + ")"; });
newPath.exit().remove();
newText=text.data(force.nodes());
newText.exit().remove();
newText.select(".shadow").text(function(d){return d.name;});
newText.select(".write").text(function(d){return d.name;});
});
});
// Use elliptical arc path segments to doubly-encode directionality.
function tick() {
path.attr("d", function(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt((dx * dx)/2 + (dy * dy));
return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
});
circle.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
text.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
</script>
</body>
</html>
答案 0 :(得分:3)
在更新代码中,您应该在使用新数据进行数据绑定之前重新选择现有节点。在当前代码中,您使用的变量circle
和path
实际上是指enter
选择中新添加的节点。
在每次数据连接之前重新选择是确保您加入DOM中最新实际状态的最佳方法:
svg.selectAll("path")
.data(force.links());
svg.selectAll("circle")
.data(force.nodes());
您可能会以某种方式对圈子和路径进行分类,以便您更直接地选择它们,这样您就不会意外地在svg中选择其他路径或圈子。
另外,请注意操作输入选择与更新选择。考虑到您没有为数据连接定义密钥,尤其如此,这意味着默认情况下将使用索引,从而导致现有节点使用新数据进行更新。例如,在您的代码中,您只在新添加的节点上设置class
属性,您可能希望在所有节点上更新它。
本教程是更好地理解这一点的良好起点:Thinking with Joins。