This example demonstrates how to create a force layout of a hierarchy whose internal nodes are collapsible. Leaf nodes are shown in orange, while internal nodes (packages) are shown in blue. Clicking on an internal node (without dragging) causes that node to expand or collapse, toggling the visibility of its descendant nodes.
Last active
July 14, 2024 15:42
-
-
Save mbostock/1093130 to your computer and use it in GitHub Desktop.
Collapsible Force Layout
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
license: gpl-3.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "flare", | |
"children": [ | |
{ | |
"name": "analytics", | |
"children": [ | |
{ | |
"name": "cluster", | |
"children": [ | |
{"name": "AgglomerativeCluster", "size": 3938}, | |
{"name": "CommunityStructure", "size": 3812}, | |
{"name": "HierarchicalCluster", "size": 6714}, | |
{"name": "MergeEdge", "size": 743} | |
] | |
}, | |
{ | |
"name": "graph", | |
"children": [ | |
{"name": "BetweennessCentrality", "size": 3534}, | |
{"name": "LinkDistance", "size": 5731}, | |
{"name": "MaxFlowMinCut", "size": 7840}, | |
{"name": "ShortestPaths", "size": 5914}, | |
{"name": "SpanningTree", "size": 3416} | |
] | |
}, | |
{ | |
"name": "optimization", | |
"children": [ | |
{"name": "AspectRatioBanker", "size": 7074} | |
] | |
} | |
] | |
} | |
] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
.node circle { | |
cursor: pointer; | |
stroke: #3182bd; | |
stroke-width: 1.5px; | |
} | |
.node text { | |
font: 10px sans-serif; | |
pointer-events: none; | |
text-anchor: middle; | |
} | |
line.link { | |
fill: none; | |
stroke: #9ecae1; | |
stroke-width: 1.5px; | |
} | |
</style> | |
<body> | |
<script src="//d3js.org/d3.v3.min.js"></script> | |
<script> | |
var width = 960, | |
height = 500, | |
root; | |
var force = d3.layout.force() | |
.linkDistance(80) | |
.charge(-120) | |
.gravity(.05) | |
.size([width, height]) | |
.on("tick", tick); | |
var svg = d3.select("body").append("svg") | |
.attr("width", width) | |
.attr("height", height); | |
var link = svg.selectAll(".link"), | |
node = svg.selectAll(".node"); | |
d3.json("graph.json", function(error, json) { | |
if (error) throw error; | |
root = json; | |
update(); | |
}); | |
function update() { | |
var nodes = flatten(root), | |
links = d3.layout.tree().links(nodes); | |
// Restart the force layout. | |
force | |
.nodes(nodes) | |
.links(links) | |
.start(); | |
// Update links. | |
link = link.data(links, function(d) { return d.target.id; }); | |
link.exit().remove(); | |
link.enter().insert("line", ".node") | |
.attr("class", "link"); | |
// Update nodes. | |
node = node.data(nodes, function(d) { return d.id; }); | |
node.exit().remove(); | |
var nodeEnter = node.enter().append("g") | |
.attr("class", "node") | |
.on("click", click) | |
.call(force.drag); | |
nodeEnter.append("circle") | |
.attr("r", function(d) { return Math.sqrt(d.size) / 10 || 4.5; }); | |
nodeEnter.append("text") | |
.attr("dy", ".35em") | |
.text(function(d) { return d.name; }); | |
node.select("circle") | |
.style("fill", color); | |
} | |
function tick() { | |
link.attr("x1", function(d) { return d.source.x; }) | |
.attr("y1", function(d) { return d.source.y; }) | |
.attr("x2", function(d) { return d.target.x; }) | |
.attr("y2", function(d) { return d.target.y; }); | |
node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); | |
} | |
function color(d) { | |
return d._children ? "#3182bd" // collapsed package | |
: d.children ? "#c6dbef" // expanded package | |
: "#fd8d3c"; // leaf node | |
} | |
// Toggle children on click. | |
function click(d) { | |
if (d3.event.defaultPrevented) return; // ignore drag | |
if (d.children) { | |
d._children = d.children; | |
d.children = null; | |
} else { | |
d.children = d._children; | |
d._children = null; | |
} | |
update(); | |
} | |
// Returns a list of all nodes under the root. | |
function flatten(root) { | |
var nodes = [], i = 0; | |
function recurse(node) { | |
if (node.children) node.children.forEach(recurse); | |
if (!node.id) node.id = ++i; | |
nodes.push(node); | |
} | |
recurse(root); | |
return nodes; | |
} | |
</script> |
Hi , what if my JSON data is in javascript string variable?, what change haas to be made in the code?
thanks...
The code works well, just make sure you refer to the correct path of the file: d3.min.js
beautiful work mbostock!
This is very nice!
But what if we want to expand the graph to certain level of parents node?
Is it possible? Thanks a lot!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
not working