防止D3在更新时添加重复 [英] Prevent D3 adding duplicates on update
问题描述
下面是一个工作的D3动画。
鼠标点击时子节点成功消失...然而,添加了重复的节点。如果你在运行下面的代码,然后折叠和打开节点,你会看到节点出现在其他的顶部!
可以看到重复的圆圈元素
只需简单的折叠和打开就是我们在这里。您的帮助非常感谢!
$ b
div class =snippetdata-lang =jsdata-hide =falsedata-console =truedata-babel =false>
< html>< head>< style> .node {cursor:pointer; font-family:HelveticaNeue-Light,Helvetica Neue Light,Helvetica Neue,Helvetica,Arial,Lucida Grande,sans-serif; font-weight:300; } .node .text {fill:white; } .ORG .circle {fill:#1d3649; } .EMR .circle {fill:#B2D0F5;中风:#5596e6; stroke-dasharray:3px,3px;不透明度:.5; } .EMR .circle:hover {fill:#5596e6; } .link {fill:none; stroke:#eee; stroke-width:1.5px; font:10px sans-serif; } .link.active {stroke:#ddd; stroke-width:2; } .arrow {fill:#666; } .arrow.active {stroke-width:0!important; }< / style>< / head>< body>< script src =https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js>< ; / script>< script src =// d3js.org/d3.v3.min.js\"> ;&script> ;<script> var dataset = {nodes:[{id:223,type:Parent,properties:{}},{id:136525,type:Child :{patient:6090,batch:70}},{id:146525,type:Child,properties:{patient:6090 :70}},{id:156525,type:Child,properties:{patient:6090 :166525,type:Child,properties:{patient:6090,batch:70}},{id:176525,type属性:{patient:6094,property:{patient:6090,batch:70}},{id:136448,type batch:70}},{id:136328,type:Child,properties:{patient:6082 id:136305,type:Child,properties:{patient:6096,batch:70}} ,properties:{patient:6093,batch:70}},{id:136299,type: ;batch:70}},{id:136261,type:Child,properties:{patient:6089 {id:136212,type:Child,properties:{patient:6087,batch:70}},{id:136115,type:儿童,属性:{patient:6078,batch:70}},{id:136113,type: 6088,batch:70}},{id:135843,type:Child,properties:{patient:6072,batch:70} }:{id:0,from:136113,to:555,;{id:555,type:Grandchild,properties:{}} properties:{}},{id:0,from:136525,至:555,properties:{}},{id:0,from:146525, :555,properties:{}},{id:0,from:156525,到:555,properties:{}},{id:0,from: 166525to:136448,properties:{}},{id:0,from:176525,到:223, from:223,to:136525,properties:{}},{id:0,from:223到:136448,properties:{}},{id :0从:223到:136328,属性:{}},{id:0,从:223到:136305, ,:,from:136525,到:136303,properties:{}},{id:0,from:223 :{}},{id:0,from:223至:136261,properties:{}},{id:0,from:223,to:136212 ,属性:{}},{id:0,from:223,到:136115,properties到:136113,properties:{}},{id:0,from:223,到:135843,properties:{}}]} var width = .width(),height = 0.95 * $(window).height(); var svg = d3.select(body)。append(svg).attr(width,width).attr(height,height); var force = d3.layout.force().size([width,height])//gravity(0.2).linkDistance(height / 6).charge(function(node){if(node.type!=='ORG ')return -2000; return -30;}); //构建箭头。 svg.append(svg:defs)。selectAll(marker).data([end])//不同的链接/路径类型可以在这里定义。 //这部分添加箭头.attr(id,function(d){return d;}).attr(viewBox,0 -5 10 10).attr(refX,12)。 attr(refY,0).attr(markerWidth,9).attr(markerHeight,5).attr(orient,auto).attr(class,arrow).append (svg:path).attr(d,M0,-5L10,0L0,5); var json = dataset; var edges = []; json.edges.forEach(function(e){var sourceNode = json.nodes.filter(function(n){return n.id === e.from;})[0],targetNode = json.nodes.filter [0]; edges.push({source:sourceNode,target:targetNode,value:e.id});}); var colors = {}; colors [23] =lightblue; colors [25] =lightgreen; colors [48] =lightyellow; colors [28] =lightblue; colors [5] =lightgreen; colors [3] =lightyellow; colors [99] =lightblue; colors [61] =lightgreen; colors [12] =lightyellow; for(var i = 0; i
[基于此的代码: http://jsfiddle.net/sheilak/9wvmL8q8/
问题在这里:
node.enter()。append(g)...
node.append(circle)...
node.append text)...
虽然您要将组添加到输入
选择,将圆形和文本添加到节点
选择,即转换元素
try:
var nodeEnter = node.enter()append(g)...
pre>
nodeEnter.append(circle)...
nodeEnter.append(text)...
在这种情况下,
nodeEnter
将等于链中最后创建的元素,是g
元素。The below is a "working" D3 animation.
Child nodes successfully disappear on mouse-click... HOWEVER, duplicates nodes ("circles") are added. If you play around with running the following code, then collapsing and opening nodes you will see nodes appearing on top of other ones!
The duplication of circle-elements can be seen in chrome-inspection too.
Just simple collapse and open is what we're after here. Your help is much appreciated ! Thank you.
(ignore the node labels - they're not important)
<html> <head> <style> .node { cursor: pointer; font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; font-weight: 300; } .node .text { fill: white; } .ORG .circle { fill: #1d3649; } .EMR .circle { fill: #B2D0F5; stroke: #5596e6; stroke-dasharray: 3px, 3px; opacity: .5; } .EMR .circle:hover { fill: #5596e6; } .link { fill: none; stroke: #eee; stroke-width: 1.5px; font: 10px sans-serif; } .link.active { stroke: #ddd; stroke-width: 2; } .arrow { fill: #666; } .arrow.active { stroke-width: 0 !important; } </style> </head> <body> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script> <script src="//d3js.org/d3.v3.min.js"></script> <script> var dataset = { "nodes": [{ "id": 223, "type": "Parent", "properties": { } }, { "id": 136525, "type": "Child", "properties": { "patient": "6090", "batch": "70" } }, { "id": 146525, "type": "Child", "properties": { "patient": "6090", "batch": "70" } }, { "id": 156525, "type": "Child", "properties": { "patient": "6090", "batch": "70" } }, { "id": 166525, "type": "Child", "properties": { "patient": "6090", "batch": "70" } }, { "id": 176525, "type": "Child", "properties": { "patient": "6090", "batch": "70" } }, { "id": 136448, "type": "Child", "properties": { "patient": "6094", "batch": "70" } }, { "id": 136328, "type": "Child", "properties": { "patient": "6082", "batch": "70" } }, { "id": 136305, "type": "Child", "properties": { "patient": "6096", "batch": "70" } }, { "id": 136303, "type": "Child", "properties": { "patient": "6093", "batch": "70" } }, { "id": 136299, "type": "Child", "properties": { "patient": "6091", "batch": "70" } }, { "id": 136261, "type": "Child", "properties": { "patient": "6089", "batch": "70" } }, { "id": 136212, "type": "Child", "properties": { "patient": "6087", "batch": "70" } }, { "id": 136115, "type": "Child", "properties": { "patient": "6078", "batch": "70" } }, { "id": 136113, "type": "Child", "properties": { "patient": "6088", "batch": "70" } }, { "id": 135843, "type": "Child", "properties": { "patient": "6072", "batch": "70" } }, { "id": 555, "type": "Grandchild", "properties": { } }], "edges": [{ "id": 0, "from": 136113, "to": 555, "properties": { } },{ "id": 0, "from": 136525, "to": 555, "properties": { } },{ "id": 0, "from": 146525, "to": 555, "properties": { } },{ "id": 0, "from": 156525, "to": 555, "properties": { } },{ "id": 0, "from": 166525, "to": 136448, "properties": { } },{ "id": 0, "from": 176525, "to": 223, "properties": { } },{ "id": 0, "from": 223, "to": 136525, "properties": { } }, { "id": 0, "from": 223, "to": 136448, "properties": { } }, { "id": 0, "from": 223, "to": 136328, "properties": { } }, { "id": 0, "from": 223, "to": 136305, "properties": { } }, { "id": 0, "from": 136525, "to": 136303, "properties": { } }, { "id": 0, "from": 223, "to": 136299, "properties": { } }, { "id": 0, "from": 223, "to": 136261, "properties": { } }, { "id": 0, "from": 223, "to": 136212, "properties": { } }, { "id": 0, "from": 223, "to": 136115, "properties": { } }, { "id": 0, "from": 223, "to": 136113, "properties": { } }, { "id": 0, "from": 223, "to": 135843, "properties": { } }] } var width = 0.975 * $(window).width(), height = 0.95 * $(window).height(); var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height); var force = d3.layout.force() .size([width, height]) //gravity(0.2) .linkDistance(height / 6) .charge(function(node) { if (node.type !== 'ORG') return -2000; return -30; }); // build the arrow. svg.append("svg:defs").selectAll("marker") .data(["end"]) // Different link/path types can be defined here .enter().append("svg:marker") // This section adds in the arrows .attr("id", function(d) { return d; }) .attr("viewBox", "0 -5 10 10") .attr("refX", 12) .attr("refY", 0) .attr("markerWidth", 9) .attr("markerHeight", 5) .attr("orient", "auto") .attr("class", "arrow") .append("svg:path") .attr("d", "M0,-5L10,0L0,5"); var json = dataset; var edges = []; json.edges.forEach(function(e) { var sourceNode = json.nodes.filter(function(n) { return n.id === e.from; })[0], targetNode = json.nodes.filter(function(n) { return n.id === e.to; })[0]; edges.push({ source: sourceNode, target: targetNode, value: e.id }); }); var colors = {}; colors[23] = "lightblue"; colors[25] = "lightgreen"; colors[48] = "lightyellow"; colors[28] = "lightblue"; colors[5] = "lightgreen"; colors[3] = "lightyellow"; colors[99] = "lightblue"; colors[61] = "lightgreen"; colors[12] = "lightyellow"; for(var i=0; i<json.nodes.length; i++) { json.nodes[i].collapsing = 0; json.nodes[i].collapsed = false; json.nodes[i].radius = json.nodes[i].id % 100; if(colors[json.nodes[i].radius] != undefined) json.nodes[i].color = colors[json.nodes[i].radius]; else json.nodes[i].color = "lightbrown"; } var link = svg.selectAll(".link"); var node = svg.selectAll(".node"); force.on("tick", function() { // make sure the nodes do not overlap the arrows link.attr("d", function(d) { // Total difference in x and y from source to target diffX = d.target.x - d.source.x; diffY = d.target.y - d.source.y; // Length of path from center of source node to center of target node pathLength = Math.sqrt((diffX * diffX) + (diffY * diffY)); // x and y distances from center to outside edge of target node offsetX = (diffX * d.target.radius) / pathLength; offsetY = (diffY * d.target.radius) / pathLength; return "M" + d.source.x + "," + d.source.y + "L" + (d.target.x - offsetX) + "," + (d.target.y - offsetY); }); node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); }); update(); function update(){ var nodes = json.nodes.filter(function(d) { return d.collapsing == 0; }); var links = edges.filter(function(d) { return d.source.collapsing == 0 && d.target.collapsing == 0; }); link = svg.selectAll(".link").data(links); link.exit().remove(); link.enter().append("path") .attr("class", "link") .attr("marker-end", "url(#end)"); node = svg.selectAll(".node").data(nodes); node.exit().remove(); node.enter().append("g") .attr("class", function(d) { return "node " + d.type }); node.append("circle") .attr("class", "circle") .attr("r", function(d) { //d.radius = 30; return d.radius }) .attr("fill", function(d) { //d.radius = 30; return d.color; }) .attr("stroke", function(d) { //d.radius = 30; return "darkgray"; }); // return a radius for path to use node.append("text") .attr("x", 0) .attr("dy", ".35em") .attr("text-anchor", "middle") .attr("class", "text") .text(function(d) { return d.type }); // On node hover, examine the links to see if their // source or target properties match the hovered node. node.on('mouseover', function(d) { link.attr('class', function(l) { if (d === l.source || d === l.target) return "link active"; else return "link inactive"; }); }); // Set the stroke width back to normal when mouse leaves the node. node.on('mouseout', function() { link.attr('class', "link"); }) .on('click', click); /** this is NOT the problem **/ function click(d) { if (!d3.event.defaultPrevented) { var inc = d.collapsed ? -1 : 1; recurse(d); function recurse(sourceNode){ //check if link is from this node, and if so, collapse edges.forEach(function(l) { if (l.source.id === sourceNode.id){ l.target.collapsing += inc; recurse(l.target); } }); } d.collapsed = !d.collapsed; } update(); } force .nodes(nodes) .links(links) .start(); } </script> </body> </html>
[code based on this: http://jsfiddle.net/sheilak/9wvmL8q8/
解决方案The problem is here:
node.enter().append("g")... node.append("circle")... node.append("text")...
Although you are adding the group to the
enter
selection, you're adding the circle and text to thenode
selection which is the transitioning elementstry:
var nodeEnter = node.enter().append("g")... nodeEnter.append("circle")... nodeEnter.append("text")...
In this case,
nodeEnter
will be equal to the last created element in the chain, which in this case is theg
element.这篇关于防止D3在更新时添加重复的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!