7

Referencing this example from Mike Bostock, I would like to apply the same idea to a bar chart where the X and Y are switched. So the labels run up and down the Y axis.

In this fiddle: http://jsfiddle.net/2eLW6/ I simply copy the wrap function from Mike's example and apply it to my chart.

function wrap(text, width) {
text.each(function() {
  var text = d3.select(this),
        words = text.text().split(/\s+/).reverse(),
        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");
    while (word = words.pop()) {
      line.push(word);
      tspan.text(line.join(" "));
      if (tspan.node().getComputedTextLength() > width) {
        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);
      }
    }
  });
}

In Mike's example applying this to an x axis, words are only put on new lines if a certain line-length is exceeded. In my fiddle it placed every single word on a new line. Also, I wonder if there is a way to move the y attribute up a little bit if a new line has been created so that it still centers?

Can anyone help me translate that wrap() function for a y axis?

The wrap() function is defined on line 203, and called from line 135 in that fiddle.

rowyourboat
  • 341
  • 1
  • 4
  • 14
  • Have you seen [this question](http://stackoverflow.com/questions/16039693/how-to-do-wordwrap-for-chart-labels-using-d3-js)? – Lars Kotthoff Jan 14 '14 at 17:15
  • 1
    For future reference this type of functionality might exist in D3 the future [GitHub D3 Text-wrapping routine](https://github.com/mbostock/d3/issues/1642) – Christopher Hackett Jan 15 '14 at 13:09

2 Answers2

1

I was looking for solutions to this problem, and found that Mike Bostock's has published a working example. The example is shown to work for the x-axis, but can easily be adapted for the y-axis. The wrap() function does not need to be modified, instead you have to apply it to the y-axis indicating the correct width-variable that may apply.

In the original example, a vertical chart, wrap() is called as follows:

svg.append("g")
  .attr("class", "x axis")
  .attr("transform", "translate(0," + height + ")")
  .call(xAxis)
.selectAll(".tick text")
  .call(wrap, x.rangeBand());

The particular details of a horizontal chart might vary, but a typical variable holding the value of the width is margin.left, as in the following snippet:

svg.append("g")
  .attr("class", "y axis")
  .call(yAxis)
.selectAll(".tick text")
  .call(wrap, margin.left);

The remaining functionality asked for in the OP would need instead to modify wrap().

wpp
  • 7,093
  • 4
  • 33
  • 65
emiguevara
  • 1,359
  • 13
  • 26
0

I cannot really get your jsfiddle to work, things seemed to be pushed way to the left, but anyway I think I identified your issue.

Regarding your first question, of why each word is getting pushed down to its own line. I believe this is because the width you are sending to wrap to determine when to do a break is quite small. In your jsfiddle, your call to wrap is:

.call(wrap, yScale.rangeBand());

yScale.rangeBand() is a pretty small value, in my resolution it was 36 pixels. This means that once your label goes above 36 pixels it will put in a line break (adding a tspan). Instead of using the rangeBand, you would want this number to be your left margin of your chart (how much space you have to the left of your y-axis).

Finally, in terms of doing vertical centering, you need to determine how many lines your label is broken down to in order to know how much to move each up. To do this, at the end of wrap you can add these two lines:

var breaks = text.selectAll("tspan")[0].length; text.selectAll("tspan").attr("y", -9 * (breaks-1));

The first piece of code gets the number of lines your label was split into. The second, selects all the tspans of your label and moves them up accordingly to center them. The -9 I use only works for the default font-size. There is a probably a better way to calculate that number to handle multiple font-sizes.


Hotamd6
  • 315
  • 3
  • 8