Anas Helalouch Anas Helalouch - 5 months ago 47
Javascript Question

How to change the root of a radial tree in D3.js

I'm having a problem with my Radial tree in D3.js

I would like to put the selected node (on click) in the center of the radial tree and have the whole tree readapt to this new setting. In my update function I can set the root to the selected node:

root = source;

However this only shows the selected node and children, but I would like to have the parent (and its children) on top of the selected node, and the children of the selected node on the bottom of it.

What I have tried is to remove the selected node from the array of children of the parent. Then pushing this changed parent as a child of the selected node, however this gives recursion errors.

var index = source.parent.children.indexOf(source);
source.parent.children.splice(0, 1);
source.parent.parent = source;

Full code here

I would appreciate any help or indication at all.

Thank you


Here is a recursive function taking care of the rearrangement:

function makeRoot(source) {
   if (!source.parent) return; //nothing to do if source is already root

   makeRoot(source.parent); //flip the higher branches recursively so that the parent becomes root of the tree 

   var index = source.parent.children.indexOf(source);
   source.parent.children.splice(index, 1);  //corrected here: you need to remove the right element from the children list
   source.parent.parent = source;
   delete source.parent; //added: now that the original parent is linked, make source the root of the tree

I'm not sure how to force the children of the original node to be drawn to the south and the rest to the north. A first thing to do would be to know on which side each node must be (this is easy: by default all nodes are "north", then visit the subtree from source, before the call to makeRoot, and tell them to be "south"). But after that, I'm not familiar enough with the tree layout from d3 to enforce the "north/south" constraint.

update for the north-south orientation, you can try the following:

  • keep a pointer oldParent=source.parent to the original parent of the selected node
  • once the layout is done, and before updating the svg, compute offsetX= 90- oldParent.x (the difference between the parent position and the north axis -- NB: I'm not sure about north axis being 90°, but it should be one of 0, 90, 180 or 270.. just try them all ;) )
  • shift all nodes around by an amount of offsetX (keeping angles between 0 and 359):

    nodes.forEach(function(d) {
        d.x = (d.x + offsetX) % 360;
  • then you can update the svg: the whole tree should be rotated so that the original parent points north ... which should be enough to get the visual effect you wanted.

update #2 Another way to make the north-south alignment.

See Fiddle here:

The idea is to compute the angle of the left-most and right-most leaf among the original children, and rotate the whole diagram until the average of these two angles points south. It means you need to keep track of the "original" children in the tree, this is done with my d.real_children property. Functions leftmostLeaf and rightmostLeaf are straightforward (see the fiddle).

  function rotateSouth(source, nodes) {
    var lx = leftmostLeaf(source);
    var rx = rightmostLeaf(source);

    if (lx>rx) rx += 360; //this happens if the interval overlap angle 0

    var offsetX= 180- (lx+rx)/2; //the median point (=(lx+rx)/2) should be pushed south (=180)

    nodes.forEach(function(d) {
      d.x = (d.x + offsetX) % 360; //rotate every node by the same offset