How to fit variable length tick labels on a D3 line chart?

d3.js, javascript, svg

Solution

You can do this by appending the text for the largest label, measuring it and removing it immediately afterwards:

var maxLabel = d3.max(data, function(d) { return d.close; }),
    maxWidth;
svg.append("text").text(maxLabel)
   .each(function() { maxWidth = this.getBBox().width; })
   .remove();

Then you can use that width to do the translation of the `g` element:

svg.attr("transform", "translate(" + Math.max(margin.left, maxWidth) + "," + margin.top + ")");

Complete example here.

Edit: Getting the maximum length of the actual labels is a bit more involved because you have to generate them (with the right formatting) and measure them all. This is a better way to do it though as you're measuring what's actually displayed. The code is similar:

var maxWidth = 0;
svg.selectAll("text.foo").data(y.ticks())
   .enter().append("text").text(function(d) { return y.tickFormat()(d); })
   .each(function(d) {
     maxWidth = Math.max(this.getBBox().width + yAxis.tickSize() + yAxis.tickPadding(), maxWidth);
   })
 .remove();

I'm adding the size of the tick line and the padding between tick line and label to the width here. Complete example of that here.

Problem

Here's a JSFiddle: http://jsfiddle.net/8p2yc/ (A slightly modified example from here: http://bl.ocks.org/mbostock/3883245) As you can see in the JSFiddle tick labels along the y axis do not fit in the svg. I know I can increase the left margin, but the thing is I don't know what the data will be in advance. If I just make the margin very large the chart will look awkward if the numbers are short in length. Is there a way to precompute the maximum label width when creating the chart to set the margin correctly? Or perhaps there's an entirely different solution? ``` var margin = {top: 20, right: 20, bottom: 30, left: 50}, width = 400 - margin.left - margin.right, height = 200 - margin.top - margin.bottom; 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 + ")"); ``` Thanks!

Original source