score:1

Accepted answer

A possible solution is to create a recursive function which will adjust your root data coordinates in your drawTree function.

Here is an recursive function which will stagger the nodes left and right. Note the comment where it is mentioned which part of the code controls the calculations of the coordinates.

function adjustClashes(data, siblings = 1, index = 1, radius = 20, height = 400) {
  //can the node fit in the current x level?
  // if not adjust it
  let heightneeded = siblings * radius * 2;
  if (heightneeded > height) {
    // the code in this if statement will control the calculations for your new coordinates
    // In the simplest case we adjust the nodes by staggering odd and even nodes
    if (index % 2 != 0){
      data.y = data.y + (radius * 2)
    } else {
      data.y = data.y - (radius * 2)
    }
  }

  // if there are children go deeper and perform same adjustment
  if (data.children) {
    data.children.forEach((f, i) => {
      return adjustClashes( f, data.children.length, i )
    })
  } else {
    return;
  }
  // finally return the data
  return data
}

Full snippet:

var data = {
  name: "Root",
  img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png",
  children: [
    {
      name: "3",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "4",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "1",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "2",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "1",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "2",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "1",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "2",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "1",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "2",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "1",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "2",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "1",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "2",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "1",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    },
    {
      name: "2",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    }
  ],
  parent: [
    {
      name: "1",
      img: "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
    }
  ]
};

var bgColors = [
  "#fd90b5",
  "#6ca1e9",
  "#fa975c",
  "#eb7092",
  "#f88962",
  "#a094ed",
  "#7f8de1"
];
var dr = 0;
// Left data
var data1 = {
  name: data.name,
  img: data.img,
  children: JSON.parse(JSON.stringify(data.children))
};

// Right data
var data2 = {
  name: data.name,
  img: data.img,
  children: JSON.parse(JSON.stringify(data.parent))
};

// Create d3 hierarchies
var right = d3.hierarchy(data1);
var left = d3.hierarchy(data2);

// Render both trees
drawTree(right, "right");
drawTree(left, "left");

// draw single tree
function drawTree(root, pos) {
  var refType;
  if (pos == "left") refType = "left";
  else refType = "right";

  var SWITCH_CONST = 1;
  if (pos === "left") {
    SWITCH_CONST = -1;
  }

  var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height");

  var g = svg.append("g").attr("transform", "translate(" + width / 2 + ",0)");

  var tree = d3.tree().size([height, (SWITCH_CONST * (width - 150)) / 2]);

  tree(root);

  function adjustClashes(
    data,
    siblings = 1,
    index = 1,
    radius = 20,
    height = 400
  ) {
    //can the node fit in the current x level?
    // if not adjust it
    let heightneeded = siblings * radius * 2;
    if (heightneeded > height) {
      // the code in this if statement will control the calculations for your new coordinates
      // In the simplest case we adjust the nodes by staggering odd and even nodes
      if (index % 2 != 0) {
        data.y = data.y + radius * 2;
      } else {
        data.y = data.y - radius * 2;
      }
    }

    // if there are children go deeper and perform same adjustment
    if (data.children) {
      data.children.forEach((f, i) => {
        return adjustClashes(f, data.children.length, i);
      });
    } else {
      return;
    }
    // finally return the data
    return data;
  }

  root = adjustClashes(root);

  var nodes = root.descendants();
  var links = root.links();
  nodes[0].x = height / 2;

  // Create links
  var link = g.selectAll(".link").data(links).enter();

  link
    .append("path")
    .attr("class", "link")
    .attr("d", function (d) {
      //first return returns a curve and the second will return straight lines in
      //return "M" + d.target.y + "," + d.target.x + "C" + (d.target.y + d.source.y) / 2.5 + "," + d.target.x + " " + (d.target.y + d.source.y) / 2 + "," + d.source.x + " " + d.source.y + "," + d.source.x;
      return (
        "M" +
        d.target.y +
        "," +
        d.target.x +
        "A" +
        dr +
        "," +
        dr +
        " 1,0 0 " +
        d.source.y +
        "," +
        d.source.x
      );
    });

  link
    .append("text")
    .attr("font-family", "Arial, Helvetica, sans-serif")
    .attr("fill", "Black")
    .style("font", "normal 12px Arial")
    .attr("transform", function (d) {
      return (
        "translate(" +
        (d.source.y + d.target.y) / 2 +
        "," +
        (d.source.x + d.target.x) / 2 +
        ")"
      );
    })
    .attr("dy", ".35em")
    .attr("text-anchor", "middle")
    .data(nodes)
    .text(refType);

  // Create nodes
  var node = g
    .selectAll(".node")
    .data(nodes)
    .enter()
    .append("g")
    .attr("class", function (d) {
      return "node" + (d.children ? " node--internal" : " node--leaf");
    })
    .attr("transform", function (d) {
      return "translate(" + d.y + "," + d.x + ")";
    });

  node
    .append("circle")
    .attr("class", "icon-wrap")
    .attr("x", 0)
    .attr("y", 0)
    .attr("r", 25)
    .style("fill", "black");

  node
    .append("image")
    .attr("href", (d) => d.data.img)
    .attr("x", "-25")
    .attr("y", "-25")
    .attr("height", "50")
    .attr("width", "50");

  node
    .append("text")
    .attr("dy", 45)
    .style("text-anchor", "middle")
    .text((d) => d.data.name);
}
.node circle {
  fill: #999;
}

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

.node--internal circle {
  fill: #555;
}

.link {
  fill: none;
  stroke: #555;
  stroke-opacity: 0.4;
  stroke-width: 1.5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<svg width="700" height="400"></svg>

Update

The following CodePen.io pen shows for example a 3 level staggering of child nodes and even adds a slight margin to space it out more evenly.

To do this simply add margin as a default parameter to the adjustClashes function and change the if statement as follows:

    if (heightneeded > height) {
      // the code in this if statement will control the calculations for your new coordinates
      // In the simplest case we adjust the nodes by staggering odd and even nodes
      if (index % 3 == 0) {
        data.y = data.y - radius * 2 - margin ;
      } else if (index % 3 == 1) {
        data.y = data.y;
      } else {
        data.y = data.y + radius * 2 + margin;
      }
    }

Related Query

More Query from same tag