Skip to content Skip to sidebar Skip to footer

D3: Position Text Element Dependent On Length Of Element Before

I'm stuck in d3 (or JavaScript in general). I want to make a legend with d3. The position of the 9 items should be dependent on each other. More specifically: This is my simplified

Solution 1:

You can draw text element to get bounding box on canvas. Then adjust position based on the last element's width:

svg.selectAll("text")
  .data(data).enter()
  .append("text")
  .attr("x", function(d) {
    return x_pos;
  })
  .attr("y", 50)
  .style("display", "none")
  .text(function(d) { return d; });

svg.selectAll("text")
  .style("display", "block")
  .attr("x", function(d) {
    var c_pos = x_pos;
    x_pos = x_pos + this.getBBox().width + distance;
    return c_pos;
  });

Full example: https://vida.io/documents/C5CSjbWLhoJ8rQmhF


Solution 2:

This is how I would do it.

//This will be your array of legends
var legendItems = [] 

var legendCount = legendItems.length;
var legendSectionWidth = width / (legendCount+1);
//This will be your "y" value going forward. You can assign anything you want. I have used the following if else case, you should replace it with your logic to calculate y.
var vert = 0;
if(legendPosition == "bottom"){
    if(showAxes)
        vert = height + legendVerticalPad + containerHeight*0.04;
    else
        vert = height + legendVerticalPad + containerHeight*0.02;
}

for(var i = 0; i < legendCount; i++){
    var text = svg.append('text')
        .attr('x', (i+1)*legendSectionWidth)
        .attr('y', vert)
        .attr('class', 'legend-text '+legendItems[i])
        .style('text-anchor', 'middle')
        .style('dominant-baseline', 'central')
        .text(function() {
            return legendItems[i];
        });

    var len = text[0][0].getComputedTextLength();

// you could use circles here, just change the width n height to rx and x and y to cx and cy// you could use circles here, just change the width n height to rx and x and y to cx and cy`enter code here`
//The value 5 is your choice, i use 5px between my rect and text
    svg.append('rect') 
        .attr('x', (i+1)*legendSectionWidth - len/2 - 5 - legendRectSize)
        .attr('y', vert - legendRectSize/2)
        .attr('width', legendRectSize)
        .attr('height', legendRectSize)
        .attr('class', function () { return 'legend '+ legendItems[i];} )
        .attr('label', function()  {
            return legendItems[i]; 
        })
}

The result is something like this Sample legends

The following images prove that the legends(combo of rect and text) are equi-distant from each and place right in the center of the provided width. And with this logic, no matter what is the no of legends you need, all will be placed equi-distant from each other and show up right in the middle of the screen

5 legends

4 legends

3 legends

2 legends

1 legend

I hope this helps.


Solution 3:

First off, d refers to an individual, bound data point, while i refers to its index in the dataset. To look at the previous data point, you would need to reference the original dataset, rather than the provided datapoint.

Assuming you had:

var dataset = ["Africa","Asia", "Caribbean", "Central America", "Europe", "Middle East", "North America", "Oceania", "South America"];

d3.select('.foo').data(dataset)....

You would want to change your d[i - 1] references in your position handler to dataset[i - 1]

With that fixed, your first element will still blow up, since it's at dataset[0]. dataset[i - 1] is dataset[-1] in that case.

You could change your return statement to:

return i ? (i * 40 + dataset[i-1].length + 7) : i;

Post a Comment for "D3: Position Text Element Dependent On Length Of Element Before"