Skip to content

Instantly share code, notes, and snippets.

@jbothma
Last active March 20, 2019 13:55
Show Gist options
  • Select an option

  • Save jbothma/2a01ee66d5ac64aa285a00f1724814d3 to your computer and use it in GitHub Desktop.

Select an option

Save jbothma/2a01ee66d5ac64aa285a00f1724814d3 to your computer and use it in GitHub Desktop.
d3 v4 treemap with wrapping
<!DOCTYPE html>
<meta charset="utf-8">
<head>
<title>Treemap layout (with labels)</title>
</head>
<style>
rect {
fill: cadetblue;
opacity: 0.3;
stroke: white;
}
text {
font-family: "Helvetica Neue", Helvetica, sans-serif;
fill: white;
font-size: 10px;
}
html, body {
overflow: hidden;
margin: 0;
font-family: "Helvetica Neue", Helvetica;
}
.labelbody {
margin: 2px;
}
foreignObject {
font: 10px sans-serif;
text-overflow: ellipsis;
text-align: left;
word-wrap: break-word;
}
</style>
<body>
<svg width="420" height="220">
<g></g>
</svg>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.2/d3.js"></script>
<script>
var data = {
children: [
{
name: "Social Development",
value: 400
},
{
name: "Police",
value: 300
},
{
name: "Cooperative Governance & Traditional Affairs",
value: 300
},
{
name: "Basic Edication",
value: 100
},
{
name: "Higher Education & Training & something & something else",
value: 50
}
]
};
var treemapLayout = d3.treemap().size([400, 200]);
var rootNode = d3.hierarchy(data);
rootNode.sum(function(d) {
return d.value;
});
treemapLayout(rootNode);
var paddingAllowance = 2;
var nodes = d3
.select("svg g")
.selectAll("g")
.data(rootNode.descendants())
.enter()
.append("g")
.attr("transform", function(d) {
return "translate(" + [d.x0, d.y0] + ")";
});
nodes
.append("rect")
.attr("width", function(d) {
return d.x1 - d.x0;
})
.attr("height", function(d) {
return d.y1 - d.y0;
});
if (window["isIE"]) {
// IE sucks so you have to manually truncate the labels here
nodes
.append("text")
.attr("class", "foreignObject")
.attr("transform", "translate(3, 13)")
.text(function(d) {
return (d.y1 - d.y0) < 16 ? "" : d.data.name;
})
.filter(function(d) {
d.tw = this.getComputedTextLength();
return (d.x1 - d.x0) < d.tw;
})
.each(function(d) {
// ridiculous routine where we test to see if label is short enough to fit
var proposedLabel = d.name;
var proposedLabelArray = proposedLabel.split("");
while (d.tw > (d.x1 - d.x0) && proposedLabelArray.length) {
// pull out 3 chars at a time to speed things up (one at a time is too slow)
proposedLabelArray.pop();
proposedLabelArray.pop();
proposedLabelArray.pop();
if (proposedLabelArray.length === 0) {
proposedLabel = "";
} else {
proposedLabel = proposedLabelArray.join("") + "..."; // manually truncate with ellipsis
}
d3.select(this).text(proposedLabel);
d.tw = this.getComputedTextLength();
}
});
} else {
// normal browsers use these labels;
// using foreignObject inside SVG allows use of wrapping text inside
// divs rather than less-flexible svg-text labels
nodes
.append("foreignObject")
.attr("class", "foreignObject")
.attr("width", function(d) {
return (d.x1 - d.x0) - paddingAllowance;
})
.attr("height", function(d) {
return (d.y1 - d.y0) - paddingAllowance;
})
.append("xhtml:body")
.attr("class", "labelbody")
.append("div")
.attr("class", "label")
.text(function(d) {
return d.data.name;
})
.attr("text-anchor", "middle");
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment