I\'m trying to use the D3 Tree Layout to create a family tree of sorts and one of the things I noticed is that when I have many children nodes, it would stretch out horizont
Check out this fiddle:
http://jsfiddle.net/dyXzu/
I took the sample code from http://bl.ocks.org/mbostock/4339083 and made some modifications. Note that in the example, x and y are switched when drawing so the layout appears as a vertical tree.
The important thing I did was modifying the depth calculator:
Original:
// Normalize for fixed-depth.
nodes.forEach(function(d) { d.y = d.depth * 180; });
Fixed:
// Normalize for fixed-depth.
nodes.forEach(function (d) {
d.y = d.depth * 180;
if (d.parent != null) {
d.x = d.parent.x - (d.parent.children.length-1)*30/2
+ (d.parent.children.indexOf(d))*30;
}
// if the node has too many children, go in and fix their positions to two columns.
if (d.children != null && d.children.length > 4) {
d.children.forEach(function (d, i) {
d.y = (d.depth * 180 + i % 2 * 100);
d.x = d.parent.x - (d.parent.children.length-1)*30/4
+ (d.parent.children.indexOf(d))*30/2 - i % 2 * 15;
});
}
});
Basically, I manually calculate the position of each node, overriding d3's default node positioning. Note that now there's no auto-scaling for x. You could probably figure this out manually by first going through and counting open nodes (d.children is not null if they exist, d._children stores the nodes when they are closed), and then adding up the total x.
Nodes with children in the two-column layout look a little funky, but changing the line-drawing method should improve things.
You could dynamically count the children and adjust the width/height of the DOM element accordingly. I would recommend counting the maximum number of children in any level, which can be calculated using the depth
property provided by d3.tree.nodes(), or just by recursively going down the tree. Modifying some code which was in a previous SO answer, you could have something like this:
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);
var newHeight = d3.max(levelWidth) * 40;
tree = tree.size([Math.max(newHeight, 660), w]);
document.getElementById("#divID").setAttribute("height", Math.max(newHeight, 660) + 50);
Or using the nodes()
method.
var nodes = tree.nodes(root), arr = [];
for(i = 0; i < nodes.length; i++)
arr[nodes[i].depth]++;
var max = 0;
for(i = 0; i <nodes.length; i++)
if(arr[i] > max)
max = arr[i];
$('#elementOfChoice').css("height", max*40);
var newHeight = max * 40;
tree = tree.size([Math.max(newHeight, 660), w]);