d3.v3 Horizontal Tree Structure with Collapsible Boxes - javascript

I am trying to get the tree nodes to line up next to the lines (paths), I cannot work it out.
This was a top to bottom tree and I have tried to flip into a left to right tree but I think the calculations are off.
Any help would be really appreciated.
Please see screenshot and code in Plunker. http://plnkr.co/edit/9pxJLz?p=preview
var margin = {
top: 20,
right: 120,
bottom: 20,
left: 120
},
width = 960 - margin.right - margin.left,
height = 100 - margin.top - margin.bottom;
var i = 0,
duration = 750,
rectW = 120,
rectH = 30;
var tree = d3.layout.tree().nodeSize([height, width]);
var diagonal = d3.svg.diagonal()
.projection(function(d) {
return [d.y, d.x];
});
var accountSvg = d3.select("body").append("svg")
.attr("width", 1000)
.attr("height", 1000)
.call(zm = d3.behavior.zoom().scaleExtent([0.5, 3]).on("zoom", redraw)).on("dblclick.zoom", null)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.left + ")");
d3.json("flare.json", function(error, flare) {
if (error) throw error;
root = flare;
root.x0 = height / 2;
root.y0 = 0;
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
root.children.forEach(collapse);
update(root);
});
//necessary so that zoom knows where to zoom and unzoom from
zm.translate([250, 20]);
d3.select("body").style("height", "455");
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 * 180;
});
// Update the nodes…
var node = accountSvg.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("class", "node")
.attr("transform", function(d) {
return "translate(" + source.y0 + "," + source.x0 + ")";
})
.on("click", click);
nodeEnter.append("rect")
.attr("width", rectW)
.attr("height", rectH)
.attr("stroke", "black")
.attr("stroke-width", 1)
.style("fill", function(d) {
return d._children ? "lightsteelblue" : "#fff";
});
nodeEnter.append("text")
.attr("x", rectW / 2)
.attr("y", rectH / 2)
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) {
return d.name;
});
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + d.y + "," + d.x + ")";
});
nodeUpdate.select("rect")
.attr("width", rectW)
.attr("height", rectH)
.attr("stroke", "black")
.attr("stroke-width", 1)
.style("fill", function(d) {
return d._children ? "lightsteelblue" : "#fff";
});
nodeUpdate.select("text")
.style("fill-opacity", 1)
.style("fill", '#404080');
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + source.y + "," + source.x + ")";
})
.remove();
nodeExit.select("rect")
.attr("width", rectW)
.attr("height", rectH)
.attr("stroke", "black")
.attr("stroke-width", 1);
nodeExit.select("text");
// Update the links…
var link = accountSvg.selectAll("path.link")
.data(links, function(d) {
return d.target.id;
});
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {
x: source.x0,
y: source.y0
};
return diagonal({
source: o,
target: o
});
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", function(d) {
var s = {
x: d.source.x0 + -15,
y: d.source.y0 + 120
}
var t = {
x: d.target.x + 15,
y: d.target.y + 120
}
return diagonal({
source: s,
target: t
})
});
//.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {
x: source.x,
y: source.y
};
return diagonal({
source: o,
target: o
});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
var doubleClickTime = 0;
var threshold = 200;
// Toggle children on click.
function click(d) {
var t0 = new Date();
if (t0 - doubleClickTime > threshold) {
setTimeout(function() {
if (t0 - doubleClickTime > threshold) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
}, threshold);
}
}
// Redraw for zoom
function redraw() {
//console.log("here", d3.event.translate, d3.event.scale);
accountSvg.attr("transform",
"translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
}

You're translating all the links:
.attr("transform", function (d) {
return "translate(" + rectW + "," + rectH / 2 + ")";
})
Don't do that. Instead of that, change the diagonal:
link.transition()
.duration(duration)
.attr("d", function(d) {
var s = {
y: d.source.y + rectW,
x: d.source.x + rectH / 2
};
var t = {
x: d.target.x + rectH / 2,
y: d.target.y
};
return diagonal({
source: s,
target: t
})
});
Here is the updated plunker: http://plnkr.co/edit/LnuoQY7R0tDWR4EnW1Rg?p=preview

Related

d3 v4 Issue with tree.nodeSize . Root is set to 0,0, even after translate being applied

I am trying to create a tree layout with d3 V4. This is done by following the example https://jsfiddle.net/augburto/YMa2y/ (this is in v3).
I am trying to implement this within angularjs, so this code is within a directive.
Everything works fine except that the root node gets positioned to 0,0 even though i have applied a transform (translate) on the svg.
Note that, i am using nodeSize on the d3.tree() so that i can have separation between nodes
What is going wrong here?
link: function(scope, element, attrs) {
var margin = { top: 20, right: 120, bottom: 20, left: 120},
width = 1090- margin.left - margin.right,
height = 800 - margin.top - margin.bottom;
var i = 0,
duration = 750,
rectW = 100,
rectH = 30;
var svg = d3.select(element[0]).append("svg");
svg.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + (width + margin.left + margin.right) / 2 + "," + 0 + ")");
scope.$watch('model', function(newVals, oldVals) {
if (oldVals !== newVals) {
return scope.render(newVals);
}
return {}
}, true);
scope.render = function (data) {
root = d3.hierarchy(data, function (d) { return d.children; });
root.x0 = 0;
root.y0 = height/ 2;
var treemap = d3.tree()
.nodeSize([rectW, rectH])
.separation(function (a, b) {
return a.parent == b.parent ? 1.10 : 2;
});// make separation accessor 1;
// Assigns the x and y position for the nodes
var treeData = treemap(root);
svg.selectAll('*').remove();
function diagonal(source, d) {
return "M" + source.x+ "," + source.y
+ "C" + source.x + "," + (source.y + d.y) / 2
+ " " + d.x+ "," + (source.y + d.y) / 2
+ " " + d.x + "," + d.y;
}
// Collapse after the second level
root.children.forEach(collapse);
update(root);
// Collapse the node and all it's children
function collapse(d) {
if (d.children) {
d._children = d.children
d._children.forEach(collapse)
d.children = null
}
}
//});
function update(source) {
//d3.tree().size([height, width]);
// Compute the new tree layout.
var nodes = treeData.descendants(),
links = treeData.descendants().slice(1);
// Normalize for fixed-depth.
nodes.forEach(function(d) { d.y = d.depth * 180 });
// 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("class", "node")
.attr("transform", function(d) {
return "translate(" + source.x0 + "," + source.y0 + ")";
})
.on("click", click);
nodeEnter.append("rect")
.attr("width", rectW)
.attr("height", rectH)
.attr("stroke", "black")
.attr("stroke-width", 1)
.style("fill", function(d) {
return d._children ? "lightsteelblue" : "#fff";
});
nodeEnter.append("text")
.attr("x", rectW / 2)
.attr("y", rectH / 2)
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) {
return d.data.Name;
});
// Transition nodes to their new position.
var nodeUpdate = nodeEnter.merge(node);
nodeUpdate.transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
nodeUpdate.select("rect")
.attr("width", rectW)
.attr("height", rectH)
.attr("stroke", "black")
.attr("stroke-width", 1)
.style("fill", function(d) {
return d._children ? "lightsteelblue" : "#fff";
});
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + source.x + "," + source.y + ")";
})
.remove();
nodeExit.select("rect")
.attr("width", rectW)
.attr("height", rectH)
//.attr("width", bbox.getBBox().width)""
//.attr("height", bbox.getBBox().height)
.attr("stroke", "black")
.attr("stroke-width", 1);
nodeExit.select("text");
// Update the links…
var link = svg.selectAll("path.link")
.data(links, function(d) {
return d.id;
});
// Enter any new links atet the parent's previous position.
var linkEnter = link.enter().insert("path", "g")
.attr("class", "link")
.attr("x", rectW / 2)
.attr("y", rectH / 2)
.attr("d", function (d) {
var o = {
x: source.x0,
y: source.y0
};
return diagonal(o, o)
}
);
//d3.linkVertical()
// .x(function (d) { return d.y; })
// .y(function (d) { return d.x; }));
// UPDATE
var linkUpdate = linkEnter.merge(link);
// Transition links to their new position.
linkUpdate.transition()
.duration(duration)
.attr("d", function(d) {
var s = {
x: d.x + rectW / 2,
y: d.y
};
var dest = {
x: d.parent.x + rectW / 2,
y: d.parent.y + rectH
};
return diagonal(s, dest)
});;
//d3.linkHorizontal()
// .x(function (d) { return d.y; })
// .y(function (d) { return d.x; }));
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
//.attr("d", d3.linkVertical()
//.x(function (d) { return d.y; })
//.y(function (d) { return d.x; }))
.attr("d", function (d) {
var o = {
x: source.x,
y: source.y
};
return diagonal(o, o)
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
// Toggle children on click.
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
}
}
The answer is here https://github.com/d3/d3-hierarchy/blob/master/README.md#tree_separation:
When a node size is specified, the root node is always positioned at ⟨0, 0⟩.
And here Centering of d3 tree changes when specifying nodeSize you can find the answer.

javascript d3.js start tree collapsed

Cannot figure out how to start my tree with data collapsed. Tried all solutions i could find here already. Have included the snipped of javascript.
People have mentioned changing the JSON file readme.json which i don't have.
Have also tried to swap _children and children with no success.
Any help much appreciated.
<script src="d3/d3.min.js"></script>
<script>
var treeData = [
<?php echo $contents ?>
];
// ************** Generate the tree diagram *****************
var margin = {top: 20, right: 0, bottom: 0, left: 250},
width = 1800 - margin.right - margin.left,
height = 900 - margin.top - margin.bottom;
var i = 0,
duration = 750,
root;
var tree = d3.layout.tree()
.size([height, width]);
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
var svg = d3.select("body").append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
root = treeData[0];
root.x0 = height / 2;
root.y0 = 0;
update(root);
d3.select(self.frameElement).style("height", "400px");
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 * 360; });
// 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("class", "node")
.attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
;
nodeEnter.on("click", function (d) {
click(d);
var x = d.jobdescription;
x = x.replace('\\','');
var g = d3.select(this); // The node
d3.select("body").select('div.tooltip').remove();
var div = d3.select("body").append("div")
.attr('pointer-events', 'none')
.attr("class", "tooltip")
.style("opacity", 1)
.html(x)
.style("left", (d.x + 50 + "px"))
.style("top", (d.y +"px"));
});
/*nodeEnter.on("mouseout", function (d) {
d3.select("body").select('div.tooltip').remove();
});*/
nodeEnter.append("circle")
.attr("r", 1e-6)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; })
;
nodeEnter.append("text")
.attr("x", function(d) { var i = ((d.the_length) * 7) + 25; var i2 = -(d.the_length + 10); return d.children || d._children ? i2 : i; })
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "end"; })
.text(function(d) { return d.name; })
.style("fill-opacity", 1e-6);
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
nodeUpdate.select("circle")
.attr("r", 10)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
.remove();
nodeExit.select("circle")
.attr("r", 1e-6);
nodeExit.select("text")
.style("fill-opacity", 1e-6);
// Update the links…
var link = svg.selectAll("path.link")
.data(links, function(d) { return d.target.id; });
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
// Toggle children on click.
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
</script>

Vertical tree structure orientation

I am trying to create organizational chart in vertical orientation, I found nice example here, this example is based on horizontal tree and I tried to change it vertical orientation, it shows the link between nodes vertically but keeps showing node circles horizontally.
For vertical orientation, I use this example and this one
I have setup a clean Codepen demo for this hopeing to get some help and make it work
My code
var margin = {top: 20, right: 120, bottom: 20, left: 120},
width = 960 - margin.right - margin.left,
height = 800 - margin.top - margin.bottom;
var i = 0,
duration = 750,
root;
var tree = d3.layout.tree()
.size([height, width]);
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
var svg = d3.select("body").append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.json("/mbostock/raw/4063550/flare.json", function(error, flare) {
if (error) throw error;
root = flare;
root.x0 = height / 2;
root.y0 = 0;
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
root.children.forEach(collapse);
update(root);
});
d3.select(self.frameElement).style("height", "800px");
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 * 180; });
// 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("class", "node")
.attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
.on("click", click);
nodeEnter.append("circle")
.attr("r", 1e-6)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeEnter.append("text")
.attr("x", function(d) { return d.children || d._children ? -10 : 10; })
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text(function(d) { return d.name; })
.style("fill-opacity", 1e-6);
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
nodeUpdate.select("circle")
.attr("r", 4.5)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
.remove();
nodeExit.select("circle")
.attr("r", 1e-6);
nodeExit.select("text")
.style("fill-opacity", 1e-6);
// Update the links…
var link = svg.selectAll("path.link")
.data(links, function(d) { return d.target.id; });
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
// Toggle children on click.
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
Well i didn't do much just merged the two examples:
1) example1 {collapsible horizontal node}
2) example2 {vertical layout}
So vertical layout you need to define the projection as:
var diagonal = d3.svg.diagonal()
.projection(function(d) {
return [d.x, d.y];//for vertical layout
});
Instead of
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });//for horizontal layout
Placing nodes:
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + source.x0 + "," + source.y0 + ")";//vertical layout
})
.on("click", click);
Instead of
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })//horizaontal layout
.on("click", click);
Working code here
Hope this helps

D3 Collapsible Tree how to make it vertically scrollable

I have a D3 collapsible tree, the problem is that a single node can have upto 4000 leaves. I would like to make it scrollable or expandable, i.e. the height should work for both 10 leaf nodes or several 1000.
Currently my height parameter is static, can anyone tell me how I can make it dynamic?
The following is the code:
$(function(){
var m = [20, 120, 20, 120],
w = 1280 - m[1] - m[3],
h = 80000 - m[0] - m[2],
i = 0,
root;
var tree = d3.layout.tree()
.size([h, w]);
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
var vis = d3.select("#modelPatterns").append("svg:svg")
.attr("width", w + m[1] + m[3])
.attr("height", h + m[0] + m[2])
.append("svg:g")
.attr("transform", "translate(" + m[3] + "," + m[0] + ")");
d3.json("./static/data/type1Tree.json", function(json) {
root = json;
root.x0 = h / 2;
root.y0 = 0;
function toggleAll(d) {
if (d.children) {
d.children.forEach(toggleAll);
toggle(d);
}
}
// Initialize the display to show a few nodes.
root.children.forEach(toggleAll);
//toggle(root.children[1]);
//toggle(root.children[1].children[2]);
//toggle(root.children[9]);
//toggle(root.children[9].children[0]);
update(root);
});
In case it helps someone, I used the zoom function which enabled panning and zooming. This may suffice for what you're looking for when trying to make the graph scrollable so to speak.
D3.select('svg')
.call(D3.zoom()
.scaleExtent([-5, 8])
.extent([[0, 0], [300, 300]])
.on('zoom', () => {
D3.selectAll('g')
.attr('transform', D3.event.transform);
if (has('root.children', this) && this.root.children.length > 50) {
this.updateAfterInit(this.root);
} else {
this.update(this.root);
}
// this.centerNode(this.root);
})
.filter(() => {
const foundNode = this.N.findNodeByID(D3.event.srcElement.id.split('_')[1])
if ( !!foundNode && D3.event.type === 'dblclick' && foundNode.data.type === 'SearchRelationspec') {
return false;
} else {
return !D3.event.target.classList.contains('drawarea') && D3.event.type === 'dblclick';
}
})
You can modify your update function like below code as per your need...
function update(source) {
var duration = d3.event && d3.event.altKey ? 5000 : 500;
// compute the new height
var levelWidth = [1];
var childCount = function(level, n) {
if(n.children && n.children.length > 0) {
if(levelWidth.length <= level + 1) levelWidth.push(0);
levelWidth[level+1] += n.children.length;
n.children.forEach(function(d) {`
childCount(level + 1, d);
});
}
};
childCount(0, root);
newHeight = d3.max(levelWidth) * 60; // 20 pixels per line
tree = tree.size([newHeight, width]);
d3.select("svg").remove();//TO REMOVE THE ALREADY SVG CONTENTS AND RELOAD ON EVERY UPDATE
svg = d3.select("body").append("svg");
svg.attr("width", width + margin.right + margin.left)
.attr("height", newHeight + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// 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 * 180; });
// 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("class", "node")
.attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
.on("click", click);
nodeEnter.append("circle")
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeEnter.append("text")
.attr("x", function(d) { return d.children || d._children ? -10 : 10; })
.attr("dy", "-.75em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text(function(d) { return d.name; })
.style("fill-opacity", 1e-2);
nodeEnter.append("text")
.attr("x", function(d) { return d.children || d._children ? -10 : 10; })
.attr("dy", "1.00em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text(function(d) { return d.info1; })
.style("fill-opacity", 1e-2);
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
nodeUpdate.select("circle")
.attr("r", 6)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeUpdate.selectAll("text")
.style("fill-opacity", 4);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
.remove();
nodeExit.selectAll("text")
.style("fill-opacity", 1e-6);
// Update the links…
var link = svg.selectAll("path.link")
.data(links, function(d) { return d.target.id; });
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
It will give you a dynamic size tree and serve for any number of nodes in the tree.
I hope this will solve your purpose.

D3 Tree Layout - Distance between child nodes that have children

Trying to get the tree layout in D3 to render the child nodes with children closer together. Here is the code:
var margin = {
top: 20,
right: 120,
bottom: 20,
left: 120
},
width = 960 - margin.right - margin.left,
height = 800 - margin.top - margin.bottom;
var i = 0,
duration = 750,
rectW = 185,
rectH = 45;
var tree = d3.layout.tree()
.nodeSize([200, 40]);
var diagonal = d3.svg.diagonal()
.projection(function (d) {
return [d.x + rectW / 2, d.y + rectH / 2];
});
var svg = d3.select("#body").append("svg").attr("width", 1000).attr("height", 1000)
.call(zm = d3.behavior.zoom().scaleExtent([0,1]).on("zoom", redraw))
.append("g")
.attr("transform", "translate(" + 30 + "," + 20 + ")");
//necessary so that zoom knows where to zoom and unzoom from
zm.translate([350, 20]);
root.x0 = width / 2;
root.y0 = 0;
update(root);
d3.select("#body").style("height", "800px");
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 * 120);
});
// 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("class", "node")
.attr("transform", function (d) {
return "translate(" + source.x0 + "," + source.y0 + ")";
})
.on("click", click);
// Add rectangles to nodes
nodeEnter.append("rect")
.attr("width", function (d) {
return rectW;
//return d._children ? "lightsteelblue" : "#fff";
})
.attr("height", rectH)
.attr("class", function (d) {
return "rect-" + d.state;
});
// Add text to nodes
nodeEnter.append("text")
.attr("x", rectW / 2)
.attr("y", rectH / 2)
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function (d) {
return d.name;
});
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
});
nodeUpdate.select("rect")
.attr("width", rectW)
.attr("height", rectH)
.attr("class", function (d) {
return "rect-" + d.state;
});
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Update the links…
var link = svg.selectAll("path.link")
.data(links, function (d) {
return d.target.id;
});
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", function (d) {
return "link " + d.target.dest;
})
.attr("x", rectW / 2)
.attr("d", function (d) {
var o = {
x: source.x0,
y: source.y0
};
return diagonal({
source: o,
target: o
});
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function (d) {
var o = {
x: source.x,
y: source.y
};
return diagonal({
source: o,
target: o
});
}).remove();
// Update the link labels…
var linkLabel = svg.selectAll("text.link-label")
.data(links, function (d) {
return d.target.id;
});
// Enter any new links at the parent's previous position.
linkLabel.enter().insert("text", "path")
.text(function (d) {
return (d.target.state !== "open") ? null : "If " + d.target.dest;
})
.attr("class", function (d) {
return "link-label " + d.target.dest;
})
.attr("x", function (d) {
return d.target.x + rectW / 2;
})
.attr("y", function (d) {
return d.target.y + rectH * 2 - 30;
})
.attr('text-anchor', 'middle')
.style("fill-opacity", 0);;
// Transition link labels
linkLabel.transition()
.delay(duration)
.style("fill-opacity", 1);
// Stash the old positions for transition.
nodes.forEach(function (d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
// Toggle children on click.
function click(d) {
return false;
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
//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 + ")");
}
Here is a jsbin of the above code. What I would like to see is the navy-colored "Node 1" and "Node 2" be closer together while preserving the distance between the nodes without children (grey nodes).
Is this possible and how would I do so?
I found the answer. It's the separation method. This got me what I was looking for:
tree.separation(function separation(a, b) {
return a.parent == b.parent ? 1 : 1.5;
});

Categories