Built with blockbuilder.org
Last active
July 1, 2020 00:13
-
-
Save yanhann10/efe71abc27ff6ed34943cf52281443f8 to your computer and use it in GitHub Desktop.
collapsible Dendrogram
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: mit |
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 rect { | |
fill: cornflower; | |
stroke: cornflower; | |
stroke-width: 3px; | |
border-radius: 10px; | |
} | |
.node text { | |
font: 12px "HelveticaNeue", sans-serif; | |
} | |
.link { | |
fill: none; | |
stroke: #e0e0e0; | |
stroke-width: 3px; | |
stroke-dasharray:5; | |
} | |
</style> | |
<body> | |
<!-- load the d3.js library --> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script> | |
//based on https://bl.ocks.org/d3noob/43a860bc0024792f8803bba8ca0d5ecd by d3noob | |
var treeData = | |
{ | |
"name": "Event", | |
"children": [ | |
{ | |
"name": "Level 2: A", | |
"children": [ | |
{ "name": "Pos Outcome" }, | |
{ "name": "Neg outcome", | |
"children": [ | |
{ "name": "procedure" }, | |
{ "name": "alternative"}, | |
{ "name": "alternative2" } | |
] } | |
] | |
}, | |
{ "name": "Level 2: B" } | |
] | |
}; | |
// Set the dimensions and margins of the diagram | |
var margin = {top: 20, right: 90, bottom: 30, left: 90}, | |
width = 960 - margin.left - margin.right, | |
height = 500 - margin.top - margin.bottom; | |
// append the svg object to the body of the page | |
// appends a 'group' element to 'svg' | |
// moves the 'group' element to the top left margin | |
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 i = 0, | |
duration = 750, | |
root; | |
// declares a tree layout and assigns the size | |
var treemap = d3.tree().size([height, width]); | |
// Assigns parent, children, height, depth | |
root = d3.hierarchy(treeData, function(d) { return d.children; }); | |
root.x0 = height / 2; | |
root.y0 = 0; | |
// Collapse after the second level | |
root.children.forEach(collapse); | |
update(root); | |
// Collapse the node and all it's children | |
function collapse(d) { | |
if(d.children) { | |
d._children = d.children | |
d._children.forEach(collapse) | |
d.children = null | |
} | |
} | |
function update(source) { | |
// Assigns the x and y position for the nodes | |
var treeData = treemap(root); | |
// Compute the new tree layout. | |
var nodes = treeData.descendants(), | |
links = treeData.descendants().slice(1); | |
// Normalize for fixed-depth. | |
nodes.forEach(function(d){ d.y = d.depth * 180}); | |
// ****************** Nodes section *************************** | |
// Update the nodes... | |
var node = svg.selectAll('g.node') | |
.data(nodes, function(d) {return d.id || (d.id = ++i); }); | |
// Enter any new modes 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); | |
//add rect as node | |
nodeEnter.append('rect') | |
.attr('class', 'node') | |
.attr('width', 1e-6) | |
.attr('height', 1e-6) | |
.style("fill", function(d) { | |
return d._children ? "lightsteelblue" : "#fff"; | |
}); | |
var customSqr = d3.symbol().type(d3.symbolWye).size(100); | |
nodeEnter.append("path") | |
.style("stroke", "#333") | |
.style("fill", "#333") | |
.attr("d", customSqr) | |
.attr("transform", function(d) { | |
return "translate(" + 95 + "," + 10 + ")"; | |
}); | |
// Add labels for the nodes | |
nodeEnter.append('text') | |
//.attr("dy", ".35em") | |
.attr("x", function(d) { | |
return d.children || d._children ? -13 : 13; | |
}) | |
.attr("y", function(d) { | |
return 14; | |
}) | |
.attr("text-anchor", function(d) { | |
return d.children || d._children ? "end" : "start"; | |
}) | |
.text(function(d) { return d.data.name; }); | |
// UPDATE | |
var nodeUpdate = nodeEnter.merge(node); | |
// Transition to the proper position for the node | |
nodeUpdate.transition() | |
.duration(duration) | |
.attr("transform", function(d) { | |
return "translate(" + d.y + "," + d.x/1.05 + ")"; | |
}); | |
// Update the node attributes and style | |
// nodeUpdate.select('circle.node') | |
// .attr('r', 10) | |
// .style("fill", function(d) { | |
// return d._children ? "lightsteelblue" : "#fff"; | |
// }) | |
// .attr('cursor', 'pointer'); | |
nodeUpdate.select('rect.node') | |
.attr('width', 112) | |
.attr('height', 24) | |
.attr("rx", 12) | |
.attr("ry", 12) | |
.style("fill", function(d) { | |
return d._children ? "lightsteelblue" : "pink"; | |
}) | |
.attr('cursor', 'pointer'); | |
// Remove any exiting nodes | |
var nodeExit = node.exit().transition() | |
.duration(duration) | |
.attr("transform", function(d) { | |
return "translate(" + source.y + "," + source.x + ")"; | |
}) | |
.remove(); | |
// On exit reduce the node circles size to 0 | |
nodeExit.select('rectangle') | |
.attr('width', 1e-6) | |
.attr('height', 1e-6); | |
// On exit reduce the opacity of text labels | |
nodeExit.select('text') | |
.style('fill-opacity', 1e-6); | |
// ****************** links section *************************** | |
// Update the links... | |
var link = svg.selectAll('path.link') | |
.data(links, function(d) { return d.id; }); | |
// 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} | |
return diagonal(o, o) | |
}); | |
// UPDATE | |
var linkUpdate = linkEnter.merge(link); | |
// Transition back to the parent element position | |
linkUpdate.transition() | |
.duration(duration) | |
.attr('d', function(d){ return diagonal(d, d.parent) }); | |
// Remove any exiting links | |
var linkExit = link.exit().transition() | |
.duration(duration) | |
.attr('d', function(d) { | |
var o = {x: source.x, y: source.y} | |
return diagonal(o, o) | |
}) | |
.remove(); | |
// Store the old positions for transition. | |
nodes.forEach(function(d){ | |
d.x0 = d.x; | |
d.y0 = d.y; | |
}); | |
// Creates a curved (diagonal) path from parent to the child nodes | |
function diagonal(s, d) { | |
path = `M ${s.y} ${s.x} | |
C ${(s.y + d.y) / 2} ${s.x}, | |
${(s.y + d.y) / 2} ${d.x}, | |
${d.y} ${d.x}` | |
return path | |
} | |
// 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> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment