This example, using satirical data from The Onion, demonstrates how to wrap long axis labels to fit on multiple lines.
Note: The second point is not recommended practice but serves to ilustrate the variable wrap feature!
Updated to use D3v5
xxxxxxxxxx
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Wrapping Long Labels</title>
<style>
.bar {
fill: steelblue;
}
.bar:hover {
fill: brown;
}
.title {
font: bold 14px "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.6.0/d3.min.js"></script>
<script>
var margin = {top: 80, right: 180, bottom: 80, left: 180},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var padding = 0.1, outerPadding = 0.3,
x = d3.scaleOrdinal();
var y = d3.scaleLinear()
.range([height, 0]);
var xAxis = d3.axisBottom()
.scale(x);
var yAxis = d3.axisLeft()
.scale(y)
.ticks(8, "%");
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.csv("data.tsv", type).then(function (data) {
var a = alpha(data, v), //scale factor between value and bar width
mid = Midi(data, v, a), //mid-point displacement of bar i
w = Wi(data, v, a); //width of bar i
x.range(data.map(mid)); //force irregular intervals based on value
x.domain(data.map(function (d) {
return d.name;
}));
y.domain([0, d3.max(data, ww)]);
svg.append("text")
.attr("class", "title")
.attr("x", x(data[0].name))
.attr("y", -26)
.attr("shape-rendering", "crispEdges")
.text("Why Are We Leaving Facebook?");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll(".tick text")
.call(wrap, function (d, i) {
return w(i);
});
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function (d, i) {
return x(d.name) - a * d.width/2; //center the bar on the tick
})
//using x.range sets x.rangeBands to zero, compute width as a function of value
.attr("width", function (d) {
return a * d.width; //`a` already accounts for both types of padding
})
.attr("y", function (d) {
return y(d.value);
})
.attr("height", function (d) {
return height - y(d.value);
});
});
function wrap(text, width) {
text.each(function (d, i) {
var text = d3.select(this),
words = text.html(d).text().split(/\s+/).reverse(), //reset html to clear any tspans added before
word,
line = [],
lineNumber = 0,
lineHeight = 1.1, // ems
y = text.attr("y"),
dy = parseFloat(text.attr("dy")),
tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em"),
//[edit]add dWidth
dWidth = typeof width === "function" ? width(d, i) : width;
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(" "));
if (tspan.node().getComputedTextLength() > dWidth/*[edit]*/) {
line.pop();
tspan.text(line.join(" "));
line = [word];
tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
}
}
});
}
function type(d) {
d.width = +d.width;
d.value = +d.value;
return d;
}
function v(d) {
return +d.width;
}
function ww(d) {
return +d.value;
}
function alpha(values, value) {
var n = values.length, total = d3.sum(values, value);
return (width - (n - 1) * padding * width / n - 2 * outerPadding * width / n) / total
}
function Wi(values, value, alpha) {
return function (i) {
return value(values[i]) * alpha
}
}
function Midi(values, value, alpha) {
var w = Wi(values, value, alpha), n = values.length;
return function (_, i) {
var op = outerPadding * width / n, p = padding * width / n;
return op + d3.sum(values.slice(0, i), value) * alpha + i * p + w(i) / 2;
}
}
</script>
</body>
</html>
https://cdnjs.cloudflare.com/ajax/libs/d3/5.6.0/d3.min.js