D3 Tree Layout - Custom vertical layout when children exceed a certain number

I am trying to use a D3 tree layout to create a family tree, and one of the things I noticed is that when I have many child nodes, it will stretch horizontally across the screen. Ideally, I would like to have a more vertical layout for these nodes so that people cannot scroll the screen and can just look down the tree.

Here is what I see now:

enter image description here

Now this may not be so bad, but if I said 20 children, it would cover the whole screen, and this is what I want to avoid.

I saw questions like this one , but that doesn’t help me because I want a specific layout, not just size ... I have large nodes and they start to collide with each other if I try to dynamically resize the tree - shortening tree does not bring me any benefit. I definitely need a different layout for situations where there are more than a certain number of children.

This is what I foresaw / hoped for. Note that the root does not do this format because it has only 4 children. Ideally, I want if a parent had 5 or more children, this would lead to a layout below. If the root had 5 children, this would lead to such a layout, and the layout should simply stretch vertically if users wanted to see the root grandchildren (nodes A, B, C ...). If necessary, I can get a diagram: enter image description here

I found a semi-similar question regarding custom layouts for kids , and he said he needed to play around with the actual d3js code. I would like to avoid this, so I hope to find out if this is possible with d3js, as it is now, and if so, how to do it? I do not need a complete answer, but a piece of code proving that this is possible will be very useful.

If necessary, I can download a JSFiddle for people to play with.

+4
source share
2 answers

Check out this script:

http://jsfiddle.net/dyXzu/

I took a sample code from http://bl.ocks.org/mbostock/4339083 and made some changes. Note that in the example, x and y switch when drawing, so the layout is displayed as a vertical tree.

The important thing I did is change the depth calculator:

Original:

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

Fixed:

 // Normalize for fixed-depth. nodes.forEach(function (d) { dy = d.depth * 180; if (d.parent != null) { dx = 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) { dy = (d.depth * 180 + i % 2 * 100); dx = 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 the default d3 node. Note that now there is no automatic scaling for x. You could probably figure this out manually by first looking at and counting the open nodes (d.children is not null if they exist, d._children keeps the nodes when they are closed), and then adding up the total number x.

Nodes with children in a two-column layout look a little scared, but changing the line drawing method should improve the situation.

+4
source

You can dynamically count child elements and adjust the width / height of the DOM element accordingly. I would recommend calculating the maximum number of children at any level, which can be calculated using the depth property provided by d3.tree.nodes (), or simply by recursively navigating the tree. By changing the code that was in the previous SO answer, you could do 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]); 
0
source

Source: https://habr.com/ru/post/1495780/


All Articles