If you play with the collapsible tree below you'll see that when you get to the end of the tree, and expand and collapse the nodes the lines are doing some wacky stuff and I am no entirely sure what drives the behavior or if my rewriting of enter link description here is completely off base. I went off a flat data structure and used stratify to transform it into a tree layout. The only issue so far is the line transitions...any thoughts?
var data = [{
"name": "Hazer 5000",
"parent": "CFO",
"img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/stephen.jpg"
}, {
"name": "Employee 1",
"parent": "Hazer 5000",
"img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/cory.jpg"
}, {
"name": "Analytics Area",
"parent": "Hazer 5000",
"img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/matt.jpg"
}, {
"name": "Employee 2",
"parent": "Hazer 5000",
"img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/XinheZhang.jpg"
}, {
"name": "Employee 3",
"parent": "Hazer 5000",
"img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/craig.jpg"
}, {
"name": "Employee 4",
"parent": "Hazer 5000",
"img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/youri.jpg"
}, {
"name": "Intern 1",
"parent": "Analytics Area",
"img": ""
}, {
"name": "Inter 2",
"parent": "Analytics Area",
"img": ""
}, {
"name": "CFO",
"parent": null,
"img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Brett.jpg"
}, {
"name": "CPA",
"parent": "CFO",
"img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Wes.jpg"
}, {
"name": "Matt's wife",
"parent": "CPA",
"img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Amy_R.jpg"
}, {
"name": "Employee 5",
"parent": "CPA",
"img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/DavidBriley.jpg"
}, {
"name": "Employee 6",
"parent": "CPA",
"img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/BrittanyAllred_.jpg"
}, {
"name": "Employee 7",
"parent": "CPA",
"img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Shea.jpg"
}, {
"name": "Employee 8",
"parent": "Matt's wife",
"img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Mindy.jpg"
}, {
"name": "Employee 9",
"parent": "Matt's wife",
"img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/Jessica_Stacy.jpg"
}, {
"name": "Employee 10",
"parent": "Matt's wife",
"img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/FraleaneHudson.jpg"
},{
"name": "Employee 11",
"parent": "Employee 9",
"img": "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-342/MeganPierce_.jpg"
},{
"name": "Intern 3",
"parent": "Employee 8",
"img": ""
}, {
"name": "Intern 4",
"parent": "Employee 8",
"img": ""
}
];
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.tree()
.size([height, width]);
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 + ")");
var stratify = d3.stratify()
.id(function(d) {
return d.name;//This position
})
.parentId(function(d) {
return d.parent; //What position this position reports to
});
var root = stratify(data);
root.each(function(d) {
d.name = d.id; //transferring name to a name variable
d.id = i; //Assigning numerical Ids
i++;
});
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(root).descendants(),
links = nodes.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.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.merge(nodeEnter).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);
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", connector);
// Enter any new links at the parent's previous position.
var linkEnter = link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0, parent:{x: source.x0, y: source.y0}};
return connector(o);
});
// Transition links to their new position.
link.merge(linkEnter).transition()
.duration(duration)
.attr("d", connector);
// 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, parent:{x: source.x, y: source.y}};
return connector(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);
}
function connector(d) {
return "M" + d.y + "," + d.x +
"C" + (d.y + d.parent.y) / 2 + "," + d.x +
" " + (d.y + d.parent.y) / 2 + "," + d.parent.x +
" " + d.parent.y + "," + d.parent.x;
}
.node {
cursor: pointer;
}
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 1.5px;
}
.node text {
font: 10px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 1.5px;
<script src="https://d3js.org/d3.v4.min.js"></script>