Skip to content

Instantly share code, notes, and snippets.

@d3noob
Last active November 21, 2023 09:38
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 11 You must be signed in to fork a gist
  • Save d3noob/8375092 to your computer and use it in GitHub Desktop.
Save d3noob/8375092 to your computer and use it in GitHub Desktop.
Interactive d3.js tree diagram
license: mit

This is a d3.js tree diagram that incldes an interactive element as used as an example in the book D3 Tips and Tricks.

Any parent node can be clicked on to collapse the portion of the tree below it, on itself. Conversly, it can be clicked on again to regrow.

It is derived from the Mike Bostock Collapsible tree example but it is a slightly cut down version.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Tree Example</title>
<style>
.node {
cursor: pointer;
}
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 3px;
}
.node text {
font: 12px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 2px;
}
</style>
</head>
<body>
<!-- load the d3.js library -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<script>
var treeData = [
{
"name": "Top Level",
"parent": "null",
"children": [
{
"name": "Level 2: A",
"parent": "Top Level",
"children": [
{
"name": "Son of A",
"parent": "Level 2: A"
},
{
"name": "Daughter of A",
"parent": "Level 2: A"
}
]
},
{
"name": "Level 2: B",
"parent": "Top Level"
}
]
}
];
// ************** Generate the tree diagram *****************
var margin = {top: 20, right: 120, bottom: 20, left: 120},
width = 960 - margin.right - margin.left,
height = 500 - margin.top - margin.bottom;
var i = 0,
duration = 750,
root;
var tree = d3.layout.tree()
.size([height, width]);
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
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 + ")");
root = treeData[0];
root.x0 = height / 2;
root.y0 = 0;
update(root);
d3.select(self.frameElement).style("height", "500px");
function update(source) {
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),
links = tree.links(nodes);
// 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 ? -13 : 13; })
.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.transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
nodeUpdate.select("circle")
.attr("r", 10)
.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, function(d) { return d.target.id; });
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// 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};
return diagonal({source: o, target: 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);
}
</script>
</body>
</html>
@prashmalyala
Copy link

Hi, thanks for this great diagram! I was reading through the code + playing around with it on JSFiddle but am having trouble understanding how all the positioning is done. Is there a convenient way to rotate this visualization so I can represent a more hierarchical structure?

@d3noob
Copy link
Author

d3noob commented Jul 1, 2020

Hi, thanks for this great diagram! I was reading through the code + playing around with it on JSFiddle but am having trouble understanding how all the positioning is done. Is there a convenient way to rotate this visualization so I can represent a more hierarchical structure?

Sure! This example uses a pretty old version of d3. I reccomend changing to version 5 and you can find examples for a vertical layout from other examples here or you can get a full explanation of how the code works in the free book here

@prashmalyala
Copy link

Hi, thanks for this great diagram! I was reading through the code + playing around with it on JSFiddle but am having trouble understanding how all the positioning is done. Is there a convenient way to rotate this visualization so I can represent a more hierarchical structure?

Sure! This example uses a pretty old version of d3. I reccomend changing to version 5 and you can find examples for a vertical layout from other examples here or you can get a full explanation of how the code works in the free book here

The eBook helped lots; got it working. Thank you!

@SarahW91
Copy link

Hi! I would like to use a slightly modified version of this code in my larger project (released under AGPL 3.0). What kind of notice should I add to the file? Who does hold the Copyright for example? It's released unser MIT license, right?

@d3noob
Copy link
Author

d3noob commented Nov 13, 2020

Hi! I would like to use a slightly modified version of this code in my larger project (released under AGPL 3.0). What kind of notice should I add to the file? Who does hold the Copyright for example? It's released unser MIT license, right?

Hi Sarah, This example is released under the MIT licience. As such you are free to use it under that model. I make no copyright claim and really don't want to restrict anyone from using it as it is a derivative of Mike Bostocks original work and ultimately, it's there to be used and to make the World a better place, not to be hoarded :-). Please use it with my blessing and thanks.

@SarahW91
Copy link

Hi! I would like to use a slightly modified version of this code in my larger project (released under AGPL 3.0). What kind of notice should I add to the file? Who does hold the Copyright for example? It's released unser MIT license, right?

Hi Sarah, This example is released under the MIT licience. As such you are free to use it under that model. I make no copyright claim and really don't want to restrict anyone from using it as it is a derivative of Mike Bostocks original work and ultimately, it's there to be used and to make the World a better place, not to be hoarded :-). Please use it with my blessing and thanks.

Great, thanks! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment