D3.js-添加新节点并进行迁移后,旧链接不会被删除 [英] D3.js - old links not getting deleted after adding a new node and trasitioning

查看:83
本文介绍了D3.js-添加新节点并进行迁移后,旧链接不会被删除的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在努力使Treeview与某些条件逻辑一起使用。我目前有以下工作提琴:


添加子节点后:-



如您所见,有一些悬挂的链接/节点,它们并没有被删除。


  var margin = {
顶部:20,
右侧:120,
底部:20,
左:120
},
宽度= 960-margin.right-margin.left,
高度= 800-margin.top-margin 。底部;

函数generateEmptyDecisionBox(condition){
return condition ==='False'吗? [{{
name: newDecision,
id: newId,
type: decision,
value: notSure,
condition:`$ {condition}`,
},{}]:[{},{
name: newDecision,
id: newId,
type:决定,
value: notSure,
condition:`$ {condition}`,
}];
}

var selectedNode;
var selectedLink;


var root = {
name: Root,
type: decision,
children:[{
名称:分析,
条件:假,
类型:决策,
值: a + b,
孩子:[{
名称:区别,
类型:动作,
条件:真实,
value: 5,
},{
name: nonDistinction,
type: action,
condition: false ,
value: 4,
children:[],
}],
},
{
condition: True,
名称:部门,
类型:决策,
值: ab,
孩子:{
name: analytics,
condition: False,
type: decision,
value: a + b,
孩子:[{
名称:区别,
类型:动作,
条件: T rue,
value: 5,
},{
name: nonDistinction,
type: decision,
condition: false,
value: 4,
children:{
name: analytics,
condition: False,
类型:决定,
值: a + b,
子项:[{
name: distincction ,
type: action,
condition: True,
value: 5,
},{
name : nonDistinction,
type: action,
condition: false,
value: 4
}],
},
}],
},
}
]
};

var i = 0,
持续时间= 500,
rectW = 120,
rectH = 60;

var tree = d3.layout.tree()。​​nodeSize([150,90]);



//链接功能为草稿链接
var linkFunc = function(d){
console.log( linkFunc,d);
var source = {
x:d.source.x + rectW / 2,
y:d.source.y +(rectH / 2)
};
var target = {
x:d.target.x +(rectW / 2),
y:d.target.y + 3,
};

//这是直线弯曲
的地方var inflection = {
x:target.x,
y:source.y
};
var radius = 5;

var结果= M + source.x +’,’+ source.y;

if(source.x< target.x&&& d.target.type){
//子项位于父项
结果的右侧+ = 'H'+(inflection.x-半径);
}否则,如果(d.target.type){
结果+ =‘H’+(inflection.x +半径);
}否则{返回

}

//在弯曲处稍微弯曲直线
结果+ ='Q'+ inflection.x +','+ inflection.y +''+弯曲。 x +','+(inflection.y +半径);

结果+ ='V'+ target.y;
的返回结果;
}

//链接功能结束//


var对角线= d3.svg.diagonal()
.projection( function(d){
return [dx + rectW / 2,dy + rectH / 2];
});

//绘制树//
var svg = d3.select(。tree-diagram)。append( svg)。attr( width,1000).attr ( height,1000)
.call(zm = d3.behavior.zoom()。scaleExtent([1,3])。on( zoom,redraw))。append( g)
.attr( transform, translate( + 350 +, + 20 +));

//将箭头添加到下一个决策点。

svg.append( svg:defs)。selectAll( marker)
.data([ end])//可以在此处定义不同的链接/路径类型
.enter()。append( svg:marker)//此部分添加了箭头
.attr( id,字符串)
.attr( viewBox, 0 -5 10 10)
.attr( refX,15)
.attr( refY,0.5)
.attr( markerWidth,4)
。 attr( markerHeight,4)
.attr( orient, auto)
.append( svg:path)
.attr( d, M0, -5L10,0L0,5);

//必要,以便zoom知道从
进行缩放的位置zm.translate([350,20]);

root.x0 = 0;
root.y0 = 0;

更新(根);

d3.select(。tree-diagram)。style( height, 1000px);

//绘制树的结尾//

函数update(source){

//计算新的树布局。
var个节点= tree.nodes(root).reverse(),
个link = tree.links(nodes);

//归一化为固定深度。
个node.forEach(function(d){
d.y = d.depth * 90;
});

//更新节点…
var node = svg.selectAll( g.node)
.data(nodes,function(d){
return d.id ||(d.id = ++ i);
});



//在父级的先前位置输入任何新节点。
var nodeEnter = node.enter()。append( g)
.attr('transform','translate('+ source.x0 +','+ source.y0 +')' )
.attr( class, node)
.on( click,click)
// .on( blur,onNodeBlur);

nodeEnter.append('path')
.attr( d,function(d){
if(d.type ==='decision'){
返回'M 60 0 120 30 60 60 0 30 Z';
}否则if(d.type ==='action'){
返回'M 0 0 120 0 120 60 0 60 Z';
}否则{
返回'M -100 -10 -10 -10 -10 -10 -10 -10Z'
}
})。attr( stroke-width,1)
.attr('class','myPaths')
.style( fill,function(d){
return lightsteelblue;
});

nodeEnter.append( text)
.attr( x,rectW / 2)
.attr( y,rectH / 2)
.attr( dy, .35em)
.attr( text-anchor, middle)
.text(function(d){
return d.name;
});

var nodeUpdate = node.transition()
.duration(duration)
.attr( transform,function(d){
return translate( +(dx)+, +(dy)+);
});


var nodeExit = node.exit()。transition()
.duration(duration)
.attr( transform,function(d){
return translate( + source.x +, + source.y +);
})
.remove();

nodeExit.select( path)
.attr( d,function(d){
if(d.type ==='decision'){
返回'M 60 0 120 30 60 60 0 30 Z';
}否则if(d.type ==='action'){
返回'M 0 0 120 0 120 60 0 60 Z';
}否则{
返回'M -100 -10 -10 -10 -10 -10 -10 -10Z'
}
})。attr( stroke-width,1)
.attr('class','myPaths')
.style( fill,function(d){
return lightsteelblue;
});



nodeExit.select( text);


//更新链接…
var link = svg.selectAll( path.link)
.data(links,function(d){
返回d.target.id;
})。classed('link1',true);


//在父母的上一个位置输入任何新链接。
var linkEnter = link.enter()。insert( g, g)
.attr( class, link);


linkEnter.append('path')
.attr( d,linkFunc)
.on('click',function(d,i) {
selectedLink = d;
//使用本地SVG接口将边界框移至
// //计算路径的中心


var bbox = this.getBBox();
var x;
var y;

if(d.source.x< d.target.x){
//子级位于父级的右侧
x = bbox.x + bbox.width;
y = bbox.y;
plusButton
.attr('transform',' translate('+ x +','+ y +')')
.classed('hide',false);

} else {
x = bbox.x;
y = bbox.y;
plusButton
.attr('transform','translate('+ x +','+ y +')')
.classed(' hide',false);
}
})
.on('blur',function(d,i){
plusButton
.classed('hide' ,true);
})。attr( marker- end, url(#end));


//添加链接文本。

linkEnter.append('text')
.text(function(d,i){

if(d.source.x< d.target .x){
返回'True';
} else {
返回'False';
}
})。attr('transform',function(d ){
console.log(d);
/ * var bbox = this.getBBox();
x = bbox.x + bbox.width;
y = bbox.y; * /
if(d.source.x< d.target.x&& d.target.type){
console.log(在此处输入源<目标) ;
返回'translate('+(d.source.x + rectW)+','+(d.source.y + rectH)+')';
}否则if(d.target .type){
return'translate('+(d.source.x-rectW / 2)+','+(d.source.y + rectH)+')';
} else {
return;
}
});


//过渡到其新职位的链接。
link.transition()
.duration(duration)
.attr( d,linkFunc);

//过渡到其新职位的链接。

//将退出节点过渡到父级的新位置。
link.exit()。transition()
.duration(duration)
.attr( d,linkFunc)
.remove();

//存放旧的过渡职位。
个node.forEach(function(d){
d.x0 = d.x;
d.y0 = d.y;
});
}


//点选
函数click(d){

if(d.type ==='动作'){返回

}

selectedNode = d;
var m = d.x;
var h = d.y;

var m = d.x + 110;
var h = d.y + 35;

diamondImage
.attr('transform','translate('+ m +','+ h +')')
.classed('hide',false) ;

var m = d.x + 110;
var h = d.y;

矩形Shape
.attr('transform','translate('+ m +','+ h +')')
.classed('hide',false) ;

var m = d.x-20;
var h = d.y + 35;

diamondImageFalse
.attr('transform','translate('+ m +','+ h +')')
.classed('hide',false) ;

var m = d.x-20;
var h = d.y;

angleShapeFalse
.attr('transform','translate('+ m +','+ h +')')
.classed('hide',false) ;
}

/ *函数onNodeBlur(){
diamondImage
.classed('hide',true);
rectangleShape
.classed('hide',true);
diamondImageFalse
.classed('hide',true);
angleShapeFalse
.classed('hide',true);
}
* /
//重绘缩放
函数redraw(){
//console.log(\"here,d3.event.translate,d3 .event.scale);
svg.attr( transform,
translate( + d3.event.translate +) +
scale( + d3.event.scale +)) ;
}



// o通话

函数addElement(d,真){
console.log(d );

d.children =空;
d.children = generateEmptyDecisionBox(truthy);
update(root);
}

//绘制元素//

函数drawDiamond(centroid){
//从顶部
console.log开始(形心);
console.log( rectH,rectH,rectW)
//从顶部
开始var result ='M'+ centroid.x +','+(centroid.y- rectH / 2);

//向右移动
结果+ =‘L’+(centroid.x + rectW / 2)+’,’+ centroid.y;

//底部
结果+ =‘L’+ centroid.x +’,’+(centroid.y + rectH / 2);

//左
结果+ =‘L’+(centroid.x-rectW / 2)+’,’+ centroid.y;

//关闭形状
结果+ ='Z';

返回结果;
}

function drawRect(centroid){
//从左上方开始
console.log(centroid);
var result ='M'+(centroid.x-rectW / 2)+’,’+(centroid.y-rectH / 2);

//向右移
结果+ =‘h’+ rectW;

//向下
结果+ =‘v’+ rectH;

//左
结果+ =‘h-’+ rectW;

//关闭形状
结果+ ='Z';

console.log(结果);
的返回结果;
}

var plusButton = svg
.append('g')
.classed('button',true)
.classed('hide ',true)
.on('click',function(){
console.log( hello);
/ * addElement(selectedLink.source); * /
console.log(点击钻石);
console.log(将隐藏设置为true);
removeAllButtonElements();
});

plusButton
.append('rect')
.attr('transform','translate(-8,-8)')//将按钮居中于` g`
.attr('width',16)
.attr('height',16)
.attr('rx',2);

plusButton
.append('path')
.attr('d','M-6 0 H6 M0 -6 V6');

varangleShape = svg.append('g')
.classed('conditionImage',true)
.classed('hide',true)
。 on('click',function(){
removeAllButtonElements();
})

矩形形状
.append('rect')
.attr ('width',30)
.attr('height',20)
.style('fill','orange');


var diamondImage = svg.append('g')
.classed('conditionSvg',true)
.classed('hide',true)
.classed('scale',true)
.on('click',function(){
console.log(点击钻石);
console.log( 将隐藏设置为true);
addElement(selectedNode,'True');
removeAllButtonElements();
});

diamondImage
.append('path')
.attr('d','M 15 0 30 15 15 30 0 15 Z')
.style (填充,橙色);


varangleShapeFalse = svg.append('g')
.classed('conditionImage',true)
.classed('hide',true)
.on('click',function(){
console.log( rectangle clicked);
removeAllButtonElements();
})

radiusShapeFalse
.append('rect')
.attr('width',30)
.attr('height',20)
.style('fill','橙子');

var diamondImageFalse = svg.append('g')
.classed('conditionImage',true)
.classed('hide',true)
。 classed('scale',true)
.on('click',function(){
console.log(点击钻石);
console.log(设置为true);
addElement(selectedNode,'False');
removeAllButtonElements();
});

diamondImageFalse
.append('path')
.attr('d','M 15 0 30 15 15 30 0 15 Z')
.style (填充,橙色);


函数removeAllButtonElements(){
plusButton.classed('hide',true);
diamondImage.classed('hide',true);
angleShape.classed('hide',true);
diamondImageFalse.classed('hide',true);
angleShapeFalse.classed('hide',true);
}


//绘制元素结束..

  .node {
光标:指针;
大纲:无!重要;
}

.node文本{
字体:10px sans-serif;
}

.button> path {
stroke:blue;
笔划宽度:1.5;
/ *大纲:无; * /
}

.button> rect {
fill:#ddd;
中风:灰色;
笔划宽度:1px;
}

.conditionalSvg {
轮廓:无;
显示:无;
}

.hide {
display:none;
opacity:0!important;
指针事件:无;
}

.link:hover {
ouline:none!important;
cursor:指针;
笔划宽度:3px;
}

.link path {
outline:none!important;
fill:无;
中风:深灰色;
stroke-width:2px;
}

.link path:hover {
cursor:指针;
stroke-width:4px;
}

.link text {
font:10px sans-serif;
}

.colorBlue {
background-color:blue;
}

 < script src = https ://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js< / script> 
< div class = tree-diagram>< / div>


尽管正在添加新节点,而旧节点正在过渡到最新位置,但旧节点/链接不会被删除。

解决方案

请注意,您的节点运行良好,只是链接运行不正常。这是因为您使用 path.link 找到了它们,但是实际结构是 g.link path 添加具有类 link g 元素,然后添加 path 。您可以通过将 path.link 更改为 g.link 来解决此问题,但随后需要选择路径文本


我通过调用来解决此问题。 .attr('d',linkFunc)在所有所有链接上,而不仅仅是新链接,并调用 .text() .attr('transform'在所有文本上。


最后,我将文本转换更改为使用 x 目标,因为目标确定线的垂直位置,而不是源。


  var margin = {
顶部:20,
右:120,
底部:20,
左:120
},
宽度= 960-margin.right-margin.left,
高度= 800-margin.top-margin.bottom;

函数generateEmptyDecisionBox(condition){
返回条件ition === False? [{{
name: newDecision,
id: newId,
type: decision,
value: notSure,
condition:`$ {condition}`,
},{}]:[{},{
name: newDecision,
id: newId,
type:决定,
value: notSure,
condition:`$ {condition}`,
}];
}

var selectedNode;
var selectedLink;


var root = {
name: Root,
type: decision,
children:[{
名称:分析,
条件:假,
类型:决策,
值: a + b,
孩子:[{
名称:区别,
类型:动作,
条件:真实,
value: 5,
},{
name: nonDistinction,
type: action,
condition: false ,
value: 4,
children:[],
}],
},
{
condition: True,
名称:部门,
类型:决策,
值: ab,
孩子:{
name: analytics,
condition: False,
type: decision,
value: a + b,
孩子:[{
名称:区别,
类型:动作,
条件: T rue,
value: 5,
},{
name: nonDistinction,
type: decision,
condition: false,
value: 4,
children:{
name: analytics,
condition: False,
类型:决定,
值: a + b,
子项:[{
name: distincction ,
type: action,
condition: True,
value: 5,
},{
name : nonDistinction,
type: action,
condition: false,
value: 4
}],
},
}],
},
}
]
};

var i = 0,
持续时间= 500,
rectW = 120,
rectH = 60;

var tree = d3.layout.tree()。​​nodeSize([150,90]);



//链接功能为草稿链接
var linkFunc = function(d){
console.log( linkFunc,d);
var source = {
x:d.source.x + rectW / 2,
y:d.source.y +(rectH / 2)
};
var target = {
x:d.target.x +(rectW / 2),
y:d.target.y + 3,
};

//这是直线弯曲
的地方var inflection = {
x:target.x,
y:source.y
};
var radius = 5;

var结果= M + source.x +’,’+ source.y;

if(source.x< target.x&&& d.target.type){
//子项位于父项
结果的右侧+ = 'H'+(inflection.x-半径);
}否则,如果(d.target.type){
结果+ =‘H’+(inflection.x +半径);
}否则{返回

}

//在弯曲处稍微弯曲直线
结果+ ='Q'+ inflection.x +','+ inflection.y +''+弯曲。 x +','+(inflection.y +半径);

结果+ ='V'+ target.y;
的返回结果;
}

//链接功能结束//


var对角线= d3.svg.diagonal()
.projection( function(d){
return [dx + rectW / 2,dy + rectH / 2];
});

//绘制树//
var svg = d3.select(。tree-diagram)。append( svg)。attr( width,1000).attr ( height,1000)
.call(zm = d3.behavior.zoom()。scaleExtent([1,3])。on( zoom,redraw))。append( g)
.attr( transform, translate( + 350 +, + 20 +));

//将箭头添加到下一个决策点。

svg.append( svg:defs)。selectAll( marker)
.data([ end])//可以在此处定义不同的链接/路径类型
.enter()。append( svg:marker)//此部分添加了箭头
.attr( id,字符串)
.attr( viewBox, 0 -5 10 10)
.attr( refX,15)
.attr( refY,0.5)
.attr( markerWidth,4)
。 attr( markerHeight,4)
.attr( orient, auto)
.append( svg:path)
.attr( d, M0, -5L10,0L0,5);

//必要,以便zoom知道从
进行缩放的位置zm.translate([350,20]);

root.x0 = 0;
root.y0 = 0;

更新(根);

d3.select(。tree-diagram)。style( height, 1000px);

//绘制树的结尾//

函数update(source){

//计算新的树布局。
var个节点= tree.nodes(root).reverse(),
个link = tree.links(nodes);

//归一化为固定深度。
个node.forEach(function(d){
d.y = d.depth * 90;
});

//更新节点…
var node = svg.selectAll( g.node)
.data(nodes,function(d){
return d.id ||(d.id = ++ i);
});

//在父级的先前位置输入任何新节点。
var nodeEnter = node.enter()。append( g)
.attr('transform','translate('+ source.x0 +','+ source.y0 +')' )
.attr( class, node)
.on( click,click)
// .on( blur,onNodeBlur);

nodeEnter.append('path')
.attr( d,function(d){
if(d.type ==='decision'){
返回'M 60 0 120 30 60 60 0 30 Z';
}否则if(d.type ==='action'){
返回'M 0 0 120 0 120 60 0 60 Z';
}否则{
返回'M -100 -10 -10 -10 -10 -10 -10 -10Z'
}
})。attr( stroke-width,1)
.attr('class','myPaths')
.style( fill,function(d){
return lightsteelblue;
});

nodeEnter.append( text)
.attr( x,rectW / 2)
.attr( y,rectH / 2)
.attr( dy, .35em)
.attr( text-anchor, middle)
.text(function(d){
return d.name;
});

var nodeUpdate = node.transition()
.duration(duration)
.attr( transform,function(d){
return translate( +(dx)+, +(dy)+);
});


var nodeExit = node.exit()。transition()
.duration(duration)
.attr( transform,function(d){
return translate( + source.x +, + source.y +);
})
.remove();

nodeExit.select( path)
.attr( d,function(d){
if(d.type ==='decision'){
返回'M 60 0 120 30 60 60 0 30 Z';
}否则if(d.type ==='action'){
返回'M 0 0 120 0 120 60 0 60 Z';
}否则{
返回'M -100 -10 -10 -10 -10 -10 -10 -10Z'
}
})。attr( stroke-width,1)
.attr('class','myPaths')
.style( fill,function(d){
return lightsteelblue;
});



nodeExit.select( text);

//更新链接…
var link = svg.selectAll(。link)
.data(links,function(d){
return d .target.id;
})。classed('link1',true);


//在父母的上一个位置输入任何新链接。
var linkEnter = link.enter()
.insert( g, g)
.attr( class, link);

linkEnter.append('path')
.on('click',function(d,i){
selectedLink = d;
//使用原生SVG接口,以将边界框移至
//计算路径的中心


var bbox = this.getBBox();
var x;
var y;

if(d.source.x< d.target.x){
//子级位于父级的右边
x = bbox .x + bbox.width;
y = bbox.y;
plusButton
.attr('transform','translate('+ x +','+ y +')')
.classed('hide',false);

} else {
x = bbox.x;
y = bbox.y;
plusButton
.attr('transform','translate('+ x +','+ y +')')
.classed('hide',false);
}
})
.on('blur',function(d,i){
plusButton
.classed('hide',true);
})。attr( marker-end , url(#end));


//添加链接文本。
linkEnter.append(‘text’);

link.select(’path')
.attr( d,linkFunc);

link.select('text')
.text(function(d,i){
if(d.source.x< d.target.x){
返回'True';
} else {
返回'False';
}
})
.attr('transform',function(d ){
console.log(d);
if(d.source.x< d.target.x&& d.target.type){
console.log( 出现在这里以获取源<目标);
返回'translate('+(d.target.x + rectW / 2)+','+(d.source.y + rectH)+') ';
}否则if(d.target.type){
返回'translate('+(d.target.x + rectW / 2)+','+(d.source.y + rectH)+')';
} else {
return;
}
});

//过渡到其新职位的链接。
link.transition()
.duration(duration)
.attr( d,linkFunc);

//过渡到其新职位的链接。

//将退出节点过渡到父级的新位置。
link.exit()。transition()
.duration(duration)
.attr( d,linkFunc)
.remove();

//存放旧的过渡职位。
个node.forEach(function(d){
d.x0 = d.x;
d.y0 = d.y;
});
}


//点选
函数click(d){

if(d.type ==='动作'){返回

}

selectedNode = d;
var m = d.x;
var h = d.y;

var m = d.x + 110;
var h = d.y + 35;

diamondImage
.attr('transform','translate('+ m +','+ h +')')
.classed('hide',false) ;

var m = d.x + 110;
var h = d.y;

矩形Shape
.attr('transform','translate('+ m +','+ h +')')
.classed('hide',false) ;

var m = d.x-20;
var h = d.y + 35;

diamondImageFalse
.attr('transform','translate('+ m +','+ h +')')
.classed('hide',false) ;

var m = d.x-20;
var h = d.y;

angleShapeFalse
.attr('transform','translate('+ m +','+ h +')')
.classed('hide',false) ;
}

/ *函数onNodeBlur(){
diamondImage
.classed('hide',true);
rectangleShape
.classed('hide',true);
diamondImageFalse
.classed('hide',true);
angleShapeFalse
.classed('hide',true);
}
* /
//重绘缩放
函数redraw(){
//console.log(\"here,d3.event.translate,d3 .event.scale);
svg.attr( transform,
translate( + d3.event.translate +) +
scale( + d3.event.scale +)) ;
}



// o通话

函数addElement(d,真){
console.log(d );

d.children =空;
d.children = generateEmptyDecisionBox(truthy);
update(root);
}

//绘制元素//

函数drawDiamond(centroid){
//从顶部
console.log开始(形心);
console.log( rectH,rectH,rectW)
//从顶部
开始var result ='M'+ centroid.x +','+(centroid.y- rectH / 2);

//向右移动
结果+ =‘L’+(centroid.x + rectW / 2)+’,’+ centroid.y;

//底部
结果+ =‘L’+ centroid.x +’,’+(centroid.y + rectH / 2);

//左
结果+ =‘L’+(centroid.x-rectW / 2)+’,’+ centroid.y;

//关闭形状
结果+ ='Z';

返回结果;
}

function drawRect(centroid){
//从左上方开始
console.log(centroid);
var result ='M'+(centroid.x-rectW / 2)+’,’+(centroid.y-rectH / 2);

//向右移
结果+ =‘h’+ rectW;

//向下
结果+ =‘v’+ rectH;

//左
结果+ =‘h-’+ rectW;

//关闭形状
结果+ ='Z';

console.log(结果);
的返回结果;
}

var plusButton = svg
.append('g')
.classed('button',true)
.classed('hide ',true)
.on('click',function(){
console.log( hello);
/ * addElement(selectedLink.source); * /
console.log(点击钻石);
console.log(将隐藏设置为true);
removeAllButtonElements();
});

plusButton
.append('rect')
.attr('transform','translate(-8,-8)')//将按钮居中于` g`
.attr('width',16)
.attr('height',16)
.attr('rx',2);

plusButton
.append('path')
.attr('d','M-6 0 H6 M0 -6 V6');

varangleShape = svg.append('g')
.classed('conditionImage',true)
.classed('hide',true)
。 on('click',function(){
removeAllButtonElements();
})

矩形形状
.append('rect')
.attr ('width',30)
.attr('height',20)
.style('fill','orange');


var diamondImage = svg.append('g')
.classed('conditionSvg',true)
.classed('hide',true)
.classed('scale',true)
.on('click',function(){
console.log(点击钻石);
console.log( 将隐藏设置为true);
addElement(selectedNode,'True');
removeAllButtonElements();
});

diamondImage
.append('path')
.attr('d','M 15 0 30 15 15 30 0 15 Z')
.style (填充,橙色);


varangleShapeFalse = svg.append('g')
.classed('conditionImage',true)
.classed('hide',true)
.on('click',function(){
console.log( rectangle clicked);
removeAllButtonElements();
})

radiusShapeFalse
.append('rect')
.attr('width',30)
.attr('height',20)
.style('fill','橙子');

var diamondImageFalse = svg.append('g')
.classed('conditionImage',true)
.classed('hide',true)
。 classed('scale',true)
.on('click',function(){
console.log(点击钻石);
console.log(设置为true);
addElement(selectedNode,'False');
removeAllButtonElements();
});

diamondImageFalse
.append('path')
.attr('d','M 15 0 30 15 15 30 0 15 Z')
.style (填充,橙色);


函数removeAllButtonElements(){
plusButton.classed('hide',true);
diamondImage.classed('hide',true);
angleShape.classed('hide',true);
diamondImageFalse.classed('hide',true);
angleShapeFalse.classed('hide',true);
}


//绘制元素结束..

  .node {
光标:指针;
大纲:无!重要;
}

.node文本{
字体:10px sans-serif;
}

.button> path {
stroke:blue;
笔划宽度:1.5;
/ *大纲:无; * /
}

.button> rect {
fill:#ddd;
中风:灰色;
笔划宽度:1px;
}

.conditionalSvg {
轮廓:无;
显示:无;
}

.hide {
display:none;
opacity:0!important;
指针事件:无;
}

.link:hover {
ouline:none!important;
cursor:指针;
笔划宽度:3px;
}

.link path {
outline:none!important;
fill:无;
中风:深灰色;
stroke-width:2px;
}

.link path:hover {
cursor:指针;
stroke-width:4px;
}

.link text {
font:10px sans-serif;
}

.colorBlue {
background-color:blue;
}

 < script src = https ://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js< / script> 
< div class = tree-diagram>< / div>


I am working on getting a treeview working with some conditional logics. I currently have the following working fiddle: https://jsfiddle.net/zgv9ajn4/4/

From the jsfiddle you can see the below image. On selecting the node with the name "division" I am showing certain orange svgs, and on click of the right-bottom svg I am adding a new child node to the "division" node.

Before Adding child Node and on clicking of the node "division":

After Adding the Child Node:-

As you can see there are some hanging links/nodes and they are not getting removed.

var margin = {
    top: 20,
    right: 120,
    bottom: 20,
    left: 120
  },
  width = 960 - margin.right - margin.left,
  height = 800 - margin.top - margin.bottom;

function generateEmptyDecisionBox(condition) {
  return condition === 'False' ? [{
    "name": "newDecision",
    "id": "newId",
    "type": "decision",
    "value": "notSure",
    "condition": `${condition}`,
  }, {}] : [{}, {
    "name": "newDecision",
    "id": "newId",
    "type": "decision",
    "value": "notSure",
    "condition": `${condition}`,
  }];
}

var selectedNode;
var selectedLink;


var root = {
  "name": "Root",
  "type": "decision",
  "children": [{
      "name": "analytics",
      "condition": "False",
      "type": "decision",
      "value": "a+b",
      "children": [{
        "name": "distinction",
        "type": "action",
        "condition": "True",
        "value": "5",
      }, {
        "name": "nonDistinction",
        "type": "action",
        "condition": "false",
        "value": "4",
        "children": [],
      }],
    },
    {
      "condition": "True",
      "name": "division",
      "type": "decision",
      "value": "a-b",
      "children": {
        "name": "analytics",
        "condition": "False",
        "type": "decision",
        "value": "a+b",
        "children": [{
          "name": "distinction",
          "type": "action",
          "condition": "True",
          "value": "5",
        }, {
          "name": "nonDistinction",
          "type": "decision",
          "condition": "false",
          "value": "4",
          "children": {
            "name": "analytics",
            "condition": "False",
            "type": "decision",
            "value": "a+b",
            "children": [{
              "name": "distinction",
              "type": "action",
              "condition": "True",
              "value": "5",
            }, {
              "name": "nonDistinction",
              "type": "action",
              "condition": "false",
              "value": "4"
            }],
          },
        }],
      },
    }
  ]
};

var i = 0,
  duration = 500,
  rectW = 120,
  rectH = 60;

var tree = d3.layout.tree().nodeSize([150, 90]);



//LINK FUNCTION TO DRAW LINKS 
var linkFunc = function(d) {
  console.log("linkFunc", d);
  var source = {
    x: d.source.x + rectW / 2,
    y: d.source.y + (rectH / 2)
  };
  var target = {
    x: d.target.x + (rectW / 2),
    y: d.target.y + 3,
  };

  // This is where the line bends
  var inflection = {
    x: target.x,
    y: source.y
  };
  var radius = 5;

  var result = "M" + source.x + ',' + source.y;

  if (source.x < target.x && d.target.type) {
    // Child is to the right of the parent
    result += ' H' + (inflection.x - radius);
  } else if (d.target.type) {
    result += ' H' + (inflection.x + radius);
  } else {
    return;
  }

  // Curve the line at the bend slightly
  result += ' Q' + inflection.x + ',' + inflection.y + ' ' + inflection.x + ',' + (inflection.y + radius);

  result += 'V' + target.y;
  return result;
}

// END OF LINK FUNC //


var diagonal = d3.svg.diagonal()
  .projection(function(d) {
    return [d.x + rectW / 2, d.y + rectH / 2];
  });

// DRAW TREE //
var svg = d3.select(".tree-diagram").append("svg").attr("width", 1000).attr("height", 1000)
  .call(zm = d3.behavior.zoom().scaleExtent([1, 3]).on("zoom", redraw)).append("g")
  .attr("transform", "translate(" + 350 + "," + 20 + ")");

// ADD ARROW TO THE BOTTOM POINTING TO THE NEXT DECISION.

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", String)
  .attr("viewBox", "0 -5 10 10")
  .attr("refX", 15)
  .attr("refY", 0.5)
  .attr("markerWidth", 4)
  .attr("markerHeight", 4)
  .attr("orient", "auto")
  .append("svg:path")
  .attr("d", "M0,-5L10,0L0,5");

//necessary so that zoom knows where to zoom and unzoom from
zm.translate([350, 20]);

root.x0 = 0;
root.y0 = 0;

update(root);

d3.select(".tree-diagram").style("height", "1000px");

// END OF DRAW TREEE //

function update(source) {

  // Compute the new tree layout.
  var nodes = tree.nodes(root).reverse(),
    links = tree.links(nodes);

  // Normalize for fixed-depth.
  nodes.forEach(function(d) {
    d.y = d.depth * 90;
  });

  // Update the nodes…
  var node = svg.selectAll("g.node")
    .data(nodes, function(d) {
      return d.id || (d.id = ++i);
    });



  // Enter any new nodes at the parent's previous position.
  var nodeEnter = node.enter().append("g")
    .attr('transform', 'translate(' + source.x0 + ', ' + source.y0 + ')')
    .attr("class", "node")
    .on("click", click)
  //  .on("blur", onNodeBlur);

  nodeEnter.append('path')
    .attr("d", function(d) {
      if (d.type === 'decision') {
        return 'M 60 0 120 30 60 60 0 30 Z';
      } else if (d.type === 'action') {
        return 'M 0 0 120 0 120 60 0 60 Z';
      } else {
        return 'M -100 -10 -10 -10 -10 -10 -10 -10Z'
      }
    }).attr("stroke-width", 1)
    .attr('class', 'myPaths')
    .style("fill", function(d) {
      return "lightsteelblue";
    });

  nodeEnter.append("text")
    .attr("x", rectW / 2)
    .attr("y", rectH / 2)
    .attr("dy", ".35em")
    .attr("text-anchor", "middle")
    .text(function(d) {
      return d.name;
    });

  var nodeUpdate = node.transition()
    .duration(duration)
    .attr("transform", function(d) {
      return "translate(" + (d.x) + "," + (d.y) + ")";
    });


  var nodeExit = node.exit().transition()
    .duration(duration)
    .attr("transform", function(d) {
      return "translate(" + source.x + "," + source.y + ")";
    })
    .remove();

  nodeExit.select("path")
    .attr("d", function(d) {
      if (d.type === 'decision') {
        return 'M 60 0 120 30 60 60 0 30 Z';
      } else if (d.type === 'action') {
        return 'M 0 0 120 0 120 60 0 60 Z';
      } else {
        return 'M -100 -10 -10 -10 -10 -10 -10 -10Z'
      }
    }).attr("stroke-width", 1)
    .attr('class', 'myPaths')
    .style("fill", function(d) {
      return "lightsteelblue";
    });



  nodeExit.select("text");


  // Update the links…
  var link = svg.selectAll("path.link")
    .data(links, function(d) {
      return d.target.id;
    }).classed('link1', true);


  // Enter any new links at the parent's previous position.
  var linkEnter = link.enter().insert("g", "g")
    .attr("class", "link");


  linkEnter.append('path')
    .attr("d", linkFunc)
    .on('click', function(d, i) {
      selectedLink = d;
      // Use the native SVG interface to get the bounding box to
      // calculate the center of the path


      var bbox = this.getBBox();
      var x;
      var y;

      if (d.source.x < d.target.x) {
        // Child is to the right of the parent
        x = bbox.x + bbox.width;
        y = bbox.y;
        plusButton
          .attr('transform', 'translate(' + x + ', ' + y + ')')
          .classed('hide', false);

      } else {
        x = bbox.x;
        y = bbox.y;
        plusButton
          .attr('transform', 'translate(' + x + ', ' + y + ')')
          .classed('hide', false);
      }
    })
    .on('blur', function(d, i) {
      plusButton
        .classed('hide', true);
    }).attr("marker-end", "url(#end)");


  // Add Link Texts.

  linkEnter.append('text')
    .text(function(d, i) {

      if (d.source.x < d.target.x) {
        return 'True';
      } else {
        return 'False';
      }
    }).attr('transform', function(d) {
      console.log(d);
      /*            var bbox = this.getBBox();
                          x=  bbox.x + bbox.width;
                          y=   bbox.y; */
      if (d.source.x < d.target.x && d.target.type) {
        console.log("comes in here for source < target");
        return 'translate(' + (d.source.x + rectW) + ',' + (d.source.y + rectH) + ')';
      } else if (d.target.type) {
        return 'translate(' + (d.source.x - rectW / 2) + ',' + (d.source.y + rectH) + ')';
      } else {
        return;
      }
    });


  // Transition links to their new position.
  link.transition()
    .duration(duration)
    .attr("d", linkFunc);

  // Transition links to their new position.

  // Transition exiting nodes to the parent's new position.
  link.exit().transition()
    .duration(duration)
    .attr("d", linkFunc)
    .remove();

  // Stash the old positions for transition.
  nodes.forEach(function(d) {
    d.x0 = d.x;
    d.y0 = d.y;
  });
}


// ON CLICK OF NODES 
function click(d) {

  if (d.type === 'action') {
    return;
  }

  selectedNode = d;
  var m = d.x;
  var h = d.y;

  var m = d.x + 110;
  var h = d.y + 35;

  diamondImage
    .attr('transform', 'translate(' + m + ', ' + h + ')')
    .classed('hide', false);

  var m = d.x + 110;
  var h = d.y;

  rectangleShape
    .attr('transform', 'translate(' + m + ', ' + h + ')')
    .classed('hide', false);

  var m = d.x - 20;
  var h = d.y + 35;

  diamondImageFalse
    .attr('transform', 'translate(' + m + ', ' + h + ')')
    .classed('hide', false);

  var m = d.x - 20;
  var h = d.y;

  rectangleShapeFalse
    .attr('transform', 'translate(' + m + ', ' + h + ')')
    .classed('hide', false);
}

/* function onNodeBlur(){
  diamondImage
  .classed('hide', true);
  rectangleShape
    .classed('hide', true);
   diamondImageFalse
     .classed('hide',true);
   rectangleShapeFalse
    .classed('hide',true);  
}
 */
//Redraw for zoom
function redraw() {
  //console.log("here", d3.event.translate, d3.event.scale);
  svg.attr("transform",
    "translate(" + d3.event.translate + ")" +
    " scale(" + d3.event.scale + ")");
}



// oN CALL

function addElement(d, truthy) {
  console.log(d);

  d.children = null;
  d.children = generateEmptyDecisionBox(truthy);
  update(root);
}

// draw elements //

function drawDiamond(centroid) {
  // Start at the top
  console.log(centroid);
  console.log("rectH", rectH, rectW)
  // Start at the top
  var result = 'M' + centroid.x + ',' + (centroid.y - rectH / 2);

  // Go right
  result += 'L' + (centroid.x + rectW / 2) + ',' + centroid.y;

  // Bottom
  result += 'L' + centroid.x + ',' + (centroid.y + rectH / 2);

  // Left
  result += 'L' + (centroid.x - rectW / 2) + ',' + centroid.y;

  // Close the shape
  result += 'Z';

  return result;
}

function drawRect(centroid) {
  // Start at the top left
  console.log(centroid);
  var result = 'M' + (centroid.x - rectW / 2) + ',' + (centroid.y - rectH / 2);

  // Go right
  result += 'h' + rectW;

  // Go down
  result += 'v' + rectH;

  // Left
  result += 'h-' + rectW;

  // Close the shape
  result += 'Z';

  console.log(result);
  return result;
}

var plusButton = svg
  .append('g')
  .classed('button', true)
  .classed('hide', true)
  .on('click', function() {
    console.log("hello");
    /*        addElement(selectedLink.source); */
    console.log("Clicked on Diamond");
    console.log("set hide to true");
    removeAllButtonElements();
  });

plusButton
  .append('rect')
  .attr('transform', 'translate(-8, -8)') // center the button inside the `g`
  .attr('width', 16)
  .attr('height', 16)
  .attr('rx', 2);

plusButton
  .append('path')
  .attr('d', 'M-6 0 H6 M0 -6 V6');

var rectangleShape = svg.append('g')
  .classed('conditionImage', true)
  .classed('hide', true)
  .on('click', function() {
    removeAllButtonElements();
  })

rectangleShape
  .append('rect')
  .attr('width', 30)
  .attr('height', 20)
  .style('fill', 'orange');


var diamondImage = svg.append('g')
  .classed('conditionSvg', true)
  .classed('hide', true)
  .classed('scale', true)
  .on('click', function() {
    console.log("Clicked on Diamond");
    console.log("set hide to true");
    addElement(selectedNode, 'True');
    removeAllButtonElements();
  });

diamondImage
  .append('path')
  .attr('d', 'M 15 0 30 15 15 30 0 15 Z')
  .style("fill", 'orange');


var rectangleShapeFalse = svg.append('g')
  .classed('conditionImage', true)
  .classed('hide', true)
  .on('click', function() {
    console.log("rectangle clicked");
    removeAllButtonElements();
  })

rectangleShapeFalse
  .append('rect')
  .attr('width', 30)
  .attr('height', 20)
  .style('fill', 'orange');

var diamondImageFalse = svg.append('g')
  .classed('conditionImage', true)
  .classed('hide', true)
  .classed('scale', true)
  .on('click', function() {
    console.log("Clicked on Diamond");
    console.log("set hide to true");
    addElement(selectedNode, 'False');
    removeAllButtonElements();
  });

diamondImageFalse
  .append('path')
  .attr('d', 'M 15 0 30 15 15 30 0 15 Z')
  .style("fill", 'orange');


function removeAllButtonElements() {
  plusButton.classed('hide', true);
  diamondImage.classed('hide', true);
  rectangleShape.classed('hide', true);
  diamondImageFalse.classed('hide', true);
  rectangleShapeFalse.classed('hide', true);
}


// draw elements end ..

.node {
  cursor: pointer;
  outline: none !important;
}

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

.button>path {
  stroke: blue;
  stroke-width: 1.5;
  /*   outline: none;  */
}

.button>rect {
  fill: #ddd;
  stroke: grey;
  stroke-width: 1px;
}

.conditionalSvg {
  outline: none;
  display: none;
}

.hide {
  display: none;
  opacity: 0 !important;
  pointer-events: none;
}

.link:hover {
  ouline: none !important;
  cursor: pointer;
  stroke-width: 3px;
}

.link path {
  outline: none !important;
  fill: none;
  stroke: darkgray;
  stroke-width: 2px;
}

.link path:hover {
  cursor: pointer;
  stroke-width: 4px;
}

.link text {
  font: 10px sans-serif;
}

.colorBlue {
  background-color: blue;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<div class="tree-diagram"></div>

Although the new node is being added and the old nodes are being transitioned to the latest positions, the old nodes/links are not being removed.

解决方案

Note that your nodes are moving fine, just the links are not. That is because you find them using path.link, but the real structure is g.link path, since you first add a g element with class link, and then a path. You could fix that by changing path.link to g.link, but then you need to select the path and text separately.

I fixed this by calling .attr('d', linkFunc) on all links, not just the new ones, and by calling .text() and .attr('transform' on all texts.

Finally, I changed the text transform to use the x of the target, since a target determines where the line goes vertical, not the source.

var margin = {
    top: 20,
    right: 120,
    bottom: 20,
    left: 120
  },
  width = 960 - margin.right - margin.left,
  height = 800 - margin.top - margin.bottom;

function generateEmptyDecisionBox(condition) {
  return condition === 'False' ? [{
    "name": "newDecision",
    "id": "newId",
    "type": "decision",
    "value": "notSure",
    "condition": `${condition}`,
  }, {}] : [{}, {
    "name": "newDecision",
    "id": "newId",
    "type": "decision",
    "value": "notSure",
    "condition": `${condition}`,
  }];
}

var selectedNode;
var selectedLink;


var root = {
  "name": "Root",
  "type": "decision",
  "children": [{
      "name": "analytics",
      "condition": "False",
      "type": "decision",
      "value": "a+b",
      "children": [{
        "name": "distinction",
        "type": "action",
        "condition": "True",
        "value": "5",
      }, {
        "name": "nonDistinction",
        "type": "action",
        "condition": "false",
        "value": "4",
        "children": [],
      }],
    },
    {
      "condition": "True",
      "name": "division",
      "type": "decision",
      "value": "a-b",
      "children": {
        "name": "analytics",
        "condition": "False",
        "type": "decision",
        "value": "a+b",
        "children": [{
          "name": "distinction",
          "type": "action",
          "condition": "True",
          "value": "5",
        }, {
          "name": "nonDistinction",
          "type": "decision",
          "condition": "false",
          "value": "4",
          "children": {
            "name": "analytics",
            "condition": "False",
            "type": "decision",
            "value": "a+b",
            "children": [{
              "name": "distinction",
              "type": "action",
              "condition": "True",
              "value": "5",
            }, {
              "name": "nonDistinction",
              "type": "action",
              "condition": "false",
              "value": "4"
            }],
          },
        }],
      },
    }
  ]
};

var i = 0,
  duration = 500,
  rectW = 120,
  rectH = 60;

var tree = d3.layout.tree().nodeSize([150, 90]);



//LINK FUNCTION TO DRAW LINKS 
var linkFunc = function(d) {
  console.log("linkFunc", d);
  var source = {
    x: d.source.x + rectW / 2,
    y: d.source.y + (rectH / 2)
  };
  var target = {
    x: d.target.x + (rectW / 2),
    y: d.target.y + 3,
  };

  // This is where the line bends
  var inflection = {
    x: target.x,
    y: source.y
  };
  var radius = 5;

  var result = "M" + source.x + ',' + source.y;

  if (source.x < target.x && d.target.type) {
    // Child is to the right of the parent
    result += ' H' + (inflection.x - radius);
  } else if (d.target.type) {
    result += ' H' + (inflection.x + radius);
  } else {
    return;
  }

  // Curve the line at the bend slightly
  result += ' Q' + inflection.x + ',' + inflection.y + ' ' + inflection.x + ',' + (inflection.y + radius);

  result += 'V' + target.y;
  return result;
}

// END OF LINK FUNC //


var diagonal = d3.svg.diagonal()
  .projection(function(d) {
    return [d.x + rectW / 2, d.y + rectH / 2];
  });

// DRAW TREE //
var svg = d3.select(".tree-diagram").append("svg").attr("width", 1000).attr("height", 1000)
  .call(zm = d3.behavior.zoom().scaleExtent([1, 3]).on("zoom", redraw)).append("g")
  .attr("transform", "translate(" + 350 + "," + 20 + ")");

// ADD ARROW TO THE BOTTOM POINTING TO THE NEXT DECISION.

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", String)
  .attr("viewBox", "0 -5 10 10")
  .attr("refX", 15)
  .attr("refY", 0.5)
  .attr("markerWidth", 4)
  .attr("markerHeight", 4)
  .attr("orient", "auto")
  .append("svg:path")
  .attr("d", "M0,-5L10,0L0,5");

//necessary so that zoom knows where to zoom and unzoom from
zm.translate([350, 20]);

root.x0 = 0;
root.y0 = 0;

update(root);

d3.select(".tree-diagram").style("height", "1000px");

// END OF DRAW TREEE //

function update(source) {

  // Compute the new tree layout.
  var nodes = tree.nodes(root).reverse(),
    links = tree.links(nodes);

  // Normalize for fixed-depth.
  nodes.forEach(function(d) {
    d.y = d.depth * 90;
  });

  // Update the nodes…
  var node = svg.selectAll("g.node")
    .data(nodes, function(d) {
      return d.id || (d.id = ++i);
    });

  // Enter any new nodes at the parent's previous position.
  var nodeEnter = node.enter().append("g")
    .attr('transform', 'translate(' + source.x0 + ', ' + source.y0 + ')')
    .attr("class", "node")
    .on("click", click)
  //  .on("blur", onNodeBlur);

  nodeEnter.append('path')
    .attr("d", function(d) {
      if (d.type === 'decision') {
        return 'M 60 0 120 30 60 60 0 30 Z';
      } else if (d.type === 'action') {
        return 'M 0 0 120 0 120 60 0 60 Z';
      } else {
        return 'M -100 -10 -10 -10 -10 -10 -10 -10Z'
      }
    }).attr("stroke-width", 1)
    .attr('class', 'myPaths')
    .style("fill", function(d) {
      return "lightsteelblue";
    });

  nodeEnter.append("text")
    .attr("x", rectW / 2)
    .attr("y", rectH / 2)
    .attr("dy", ".35em")
    .attr("text-anchor", "middle")
    .text(function(d) {
      return d.name;
    });

  var nodeUpdate = node.transition()
    .duration(duration)
    .attr("transform", function(d) {
      return "translate(" + (d.x) + "," + (d.y) + ")";
    });


  var nodeExit = node.exit().transition()
    .duration(duration)
    .attr("transform", function(d) {
      return "translate(" + source.x + "," + source.y + ")";
    })
    .remove();

  nodeExit.select("path")
    .attr("d", function(d) {
      if (d.type === 'decision') {
        return 'M 60 0 120 30 60 60 0 30 Z';
      } else if (d.type === 'action') {
        return 'M 0 0 120 0 120 60 0 60 Z';
      } else {
        return 'M -100 -10 -10 -10 -10 -10 -10 -10Z'
      }
    }).attr("stroke-width", 1)
    .attr('class', 'myPaths')
    .style("fill", function(d) {
      return "lightsteelblue";
    });



  nodeExit.select("text");

  // Update the links…
  var link = svg.selectAll(".link")
    .data(links, function(d) {
      return d.target.id;
    }).classed('link1', true);


  // Enter any new links at the parent's previous position.
  var linkEnter = link.enter()
    .insert("g", "g")
    .attr("class", "link");

  linkEnter.append('path')
    .on('click', function(d, i) {
      selectedLink = d;
      // Use the native SVG interface to get the bounding box to
      // calculate the center of the path


      var bbox = this.getBBox();
      var x;
      var y;

      if (d.source.x < d.target.x) {
        // Child is to the right of the parent
        x = bbox.x + bbox.width;
        y = bbox.y;
        plusButton
          .attr('transform', 'translate(' + x + ', ' + y + ')')
          .classed('hide', false);

      } else {
        x = bbox.x;
        y = bbox.y;
        plusButton
          .attr('transform', 'translate(' + x + ', ' + y + ')')
          .classed('hide', false);
      }
    })
    .on('blur', function(d, i) {
      plusButton
        .classed('hide', true);
    }).attr("marker-end", "url(#end)");


  // Add Link Texts.
  linkEnter.append('text');

  link.select('path')
    .attr("d", linkFunc);
  
  link.select('text')
    .text(function(d, i) {
      if (d.source.x < d.target.x) {
        return 'True';
      } else {
        return 'False';
      }
    })
    .attr('transform', function(d) {
      console.log(d);
      if (d.source.x < d.target.x && d.target.type) {
        console.log("comes in here for source < target");
        return 'translate(' + (d.target.x + rectW / 2) + ',' + (d.source.y + rectH) + ')';
      } else if (d.target.type) {
        return 'translate(' + (d.target.x + rectW / 2) + ',' + (d.source.y + rectH) + ')';
      } else {
        return;
      }
    });

  // Transition links to their new position.
  link.transition()
    .duration(duration)
    .attr("d", linkFunc);

  // Transition links to their new position.

  // Transition exiting nodes to the parent's new position.
  link.exit().transition()
    .duration(duration)
    .attr("d", linkFunc)
    .remove();

  // Stash the old positions for transition.
  nodes.forEach(function(d) {
    d.x0 = d.x;
    d.y0 = d.y;
  });
}


// ON CLICK OF NODES 
function click(d) {

  if (d.type === 'action') {
    return;
  }

  selectedNode = d;
  var m = d.x;
  var h = d.y;

  var m = d.x + 110;
  var h = d.y + 35;

  diamondImage
    .attr('transform', 'translate(' + m + ', ' + h + ')')
    .classed('hide', false);

  var m = d.x + 110;
  var h = d.y;

  rectangleShape
    .attr('transform', 'translate(' + m + ', ' + h + ')')
    .classed('hide', false);

  var m = d.x - 20;
  var h = d.y + 35;

  diamondImageFalse
    .attr('transform', 'translate(' + m + ', ' + h + ')')
    .classed('hide', false);

  var m = d.x - 20;
  var h = d.y;

  rectangleShapeFalse
    .attr('transform', 'translate(' + m + ', ' + h + ')')
    .classed('hide', false);
}

/* function onNodeBlur(){
  diamondImage
  .classed('hide', true);
  rectangleShape
    .classed('hide', true);
   diamondImageFalse
     .classed('hide',true);
   rectangleShapeFalse
    .classed('hide',true);  
}
 */
//Redraw for zoom
function redraw() {
  //console.log("here", d3.event.translate, d3.event.scale);
  svg.attr("transform",
    "translate(" + d3.event.translate + ")" +
    " scale(" + d3.event.scale + ")");
}



// oN CALL

function addElement(d, truthy) {
  console.log(d);

  d.children = null;
  d.children = generateEmptyDecisionBox(truthy);
  update(root);
}

// draw elements //

function drawDiamond(centroid) {
  // Start at the top
  console.log(centroid);
  console.log("rectH", rectH, rectW)
  // Start at the top
  var result = 'M' + centroid.x + ',' + (centroid.y - rectH / 2);

  // Go right
  result += 'L' + (centroid.x + rectW / 2) + ',' + centroid.y;

  // Bottom
  result += 'L' + centroid.x + ',' + (centroid.y + rectH / 2);

  // Left
  result += 'L' + (centroid.x - rectW / 2) + ',' + centroid.y;

  // Close the shape
  result += 'Z';

  return result;
}

function drawRect(centroid) {
  // Start at the top left
  console.log(centroid);
  var result = 'M' + (centroid.x - rectW / 2) + ',' + (centroid.y - rectH / 2);

  // Go right
  result += 'h' + rectW;

  // Go down
  result += 'v' + rectH;

  // Left
  result += 'h-' + rectW;

  // Close the shape
  result += 'Z';

  console.log(result);
  return result;
}

var plusButton = svg
  .append('g')
  .classed('button', true)
  .classed('hide', true)
  .on('click', function() {
    console.log("hello");
    /*        addElement(selectedLink.source); */
    console.log("Clicked on Diamond");
    console.log("set hide to true");
    removeAllButtonElements();
  });

plusButton
  .append('rect')
  .attr('transform', 'translate(-8, -8)') // center the button inside the `g`
  .attr('width', 16)
  .attr('height', 16)
  .attr('rx', 2);

plusButton
  .append('path')
  .attr('d', 'M-6 0 H6 M0 -6 V6');

var rectangleShape = svg.append('g')
  .classed('conditionImage', true)
  .classed('hide', true)
  .on('click', function() {
    removeAllButtonElements();
  })

rectangleShape
  .append('rect')
  .attr('width', 30)
  .attr('height', 20)
  .style('fill', 'orange');


var diamondImage = svg.append('g')
  .classed('conditionSvg', true)
  .classed('hide', true)
  .classed('scale', true)
  .on('click', function() {
    console.log("Clicked on Diamond");
    console.log("set hide to true");
    addElement(selectedNode, 'True');
    removeAllButtonElements();
  });

diamondImage
  .append('path')
  .attr('d', 'M 15 0 30 15 15 30 0 15 Z')
  .style("fill", 'orange');


var rectangleShapeFalse = svg.append('g')
  .classed('conditionImage', true)
  .classed('hide', true)
  .on('click', function() {
    console.log("rectangle clicked");
    removeAllButtonElements();
  })

rectangleShapeFalse
  .append('rect')
  .attr('width', 30)
  .attr('height', 20)
  .style('fill', 'orange');

var diamondImageFalse = svg.append('g')
  .classed('conditionImage', true)
  .classed('hide', true)
  .classed('scale', true)
  .on('click', function() {
    console.log("Clicked on Diamond");
    console.log("set hide to true");
    addElement(selectedNode, 'False');
    removeAllButtonElements();
  });

diamondImageFalse
  .append('path')
  .attr('d', 'M 15 0 30 15 15 30 0 15 Z')
  .style("fill", 'orange');


function removeAllButtonElements() {
  plusButton.classed('hide', true);
  diamondImage.classed('hide', true);
  rectangleShape.classed('hide', true);
  diamondImageFalse.classed('hide', true);
  rectangleShapeFalse.classed('hide', true);
}


// draw elements end ..

.node {
  cursor: pointer;
  outline: none !important;
}

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

.button>path {
  stroke: blue;
  stroke-width: 1.5;
  /*   outline: none;  */
}

.button>rect {
  fill: #ddd;
  stroke: grey;
  stroke-width: 1px;
}

.conditionalSvg {
  outline: none;
  display: none;
}

.hide {
  display: none;
  opacity: 0 !important;
  pointer-events: none;
}

.link:hover {
  ouline: none !important;
  cursor: pointer;
  stroke-width: 3px;
}

.link path {
  outline: none !important;
  fill: none;
  stroke: darkgray;
  stroke-width: 2px;
}

.link path:hover {
  cursor: pointer;
  stroke-width: 4px;
}

.link text {
  font: 10px sans-serif;
}

.colorBlue {
  background-color: blue;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<div class="tree-diagram"></div>

这篇关于D3.js-添加新节点并进行迁移后,旧链接不会被删除的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆