Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiline svg text axis ticks in d3

Tags:

svg

d3.js

I am attempting to render a simple bar graph with a label for each bar as axis ticks on the x-axis. Longer labels run over each other. Since svg text elements don't support word-wrapping I have been looking into alternative solutions.

Changing the category text that goes into the labels to include the proper <tspan> elements doesn't work since the text isn't set as the innerHtml but rather just the element's raw text. I have also considered post-processing the labels to remove the text and replace it with tspans, but I haven't found an elegant way to do that yet.

Unfortunately I can't use foreignObject since I need IE9 support, but many of the same markup replacement problems would apply to that solution, anyway.

Has anyone solved this problem well in the past or have any suggestions?

like image 651
Kyle Goodwin Avatar asked Dec 07 '12 14:12

Kyle Goodwin


1 Answers

You can do the wrapping manually as in this example by Mike Bostock:

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);
      }
    }
  });
}

vis.selectAll(".tick text")
  .call(wrap, 100);
like image 93
Ilya Boyandin Avatar answered Nov 18 '22 18:11

Ilya Boyandin