I am trying to implement the horizontal bar chart using d3.js.Some of the chart labels are too long. How to do word wrap for the chart labels on y aixs?
Source code:
var data = [{"Name": "Label 1", "Count": "428275" }, { "Name": "Label 2", "Count": "365005" }, { "Name": "Label 3", "Count": "327619" }];
var m = [30, 10, 10, 310],
w = 1000 - m[1] - m[3],
h = 550 - m[0] - m[2];
var format = d3.format(",.0f");
var x = d3.scale.linear().range([0, w + 10]),
y = d3.scale.ordinal().rangeRoundBands([0, h], .4);
var xAxis = d3.svg.axis().scale(x).orient("bottom").tickSize(h),
yAxis = d3.svg.axis().scale(y).orient("left").tickSize(0);
$("#chartrendering").empty();
var svg = d3.select("#chartrendering").append("svg")
.attr("width", w + m[1] + m[3])
.attr("height", h + m[0] + m[2])
.append("g")
.attr("transform", "translate(" + m[3] + "," + m[0] + ")");
// Set the scale domain.
x.domain([0, d3.max(data, function (d) { return d.Count; })]);
y.domain(data.map(function (d) { return d.Name; }));
var bar = svg.selectAll("g.bar")
.data(data)
.enter().append("g")
.attr("class", "bar")
.attr("transform", function (d) { return "translate(0," + y(d.Name) + ")"; });
bar.append("rect")
.attr("width", function (d) { return x(d.Count); })
.attr("height", y.rangeBand());
bar.append("text")
.attr("class", "value")
.attr("x", function (d) { return x(d.Count); })
.attr("y", y.rangeBand() / 2)
.attr("dx", +55)
.attr("dy", ".35em")
.attr("text-anchor", "end")
.text(function (d) { return format(d.Count); });
svg.append("g")
.attr("class", "x axis")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
You can't do automatic word wrap in SVG. You could use
foreignObject
and HTMLdiv
s for that purpose, but that would require modifying the code that creates the axis labels. Alternatively, you can rotate the axis labels so that they have more space. See for example here for how to do that.Here is a working implementation I've written by pulling together various bits. As the other answer suggests, foreignObject is still the way to go. First the function:
This takes in a text element (t), the text content (d), and the width to wrap to. It then gets the parentNode of the
text
object, and attaches aforeignObject
node to it into which anxhtml:p
is added. TheforeignObject
is set to the desiredwidth
and offset-width/2
to center. Finally, the original text element is deleted.This can then be applied to your axis elements as follows:
Here I've used rangeBand to get the width (with *2 for 2 bars on the graph).
I was looking for solutions to this problem, and found that Mike Bostock has published a working example using D3. The example is shown to work for the x-axis, but can easily be adapted for the y-axis.
Here's a function I wrote not only to solve the y-axis word wrap problem, but also wrap word that is more than 1 line in length, and also align the corresponding 'tick' in the center of the label:
Result is like this:
See this snippet:
Main function about word wrap is this one:
wrapText
. Modified from https://bl.ocks.org/ericsoco/647db6ebadd4f4756cae and https://bl.ocks.org/mbostock/7555321Hope it can help.
Here's some code to plumb Mike Bostock's
chart()
function into angular-nvd3. For background, see https://github.com/krispo/angular-nvd3/issues/36.