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 array: var dataset = ["Africa","Asia", "Caribbean", "Central America", "Europe", "Middle East", "North America", "Oceania", "South America"];

On the x-axis, I want to draw the next text 40px futher (to the right) then the last text lable ended. My intention is to have the same space between the circles every time. So the next text is always dependent on the length of the last country name.

I tried this:

.attr("x", function(d, i) {return i * 40 + d[i-1].length + 7;})

but the console says d[i-1] is undefined.

What am I missing? How would you solve this?

Many thanks in advance! Your help is very much appreciated!


UPDATE: Actually the legend I want to draw not only consists of the text, but also little circles.

Here is the array (with hard coded x_pos as d[2]: var dataset = [ ["Africa", "#4B7985", 5], ["Asia", "#58AB86", 55], ["Caribbean", "#63A4B5", 100], ["Central America", "#818181", 165], ["Europe", "#E9726C", 255], ["Middle East", "#E3AC73", 310], ["North America", "#B65856", 383], ["Oceania", "#287E5C", 470], ["South America", "#AC8358", 530] ];

How do I draw the circles dependent on the length of the country names and get the same spacing between the cirlces?


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: <a href="https://vida.io/documents/C5CSjbWLhoJ8rQmhF" rel="nofollow">https://vida.io/documents/C5CSjbWLhoJ8rQmhF</a>


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 <a href="https://i.stack.imgur.com/ldzwL.png" rel="nofollow"><img alt="Sample legends" class="b-lazy" data-src="https://i.stack.imgur.com/ldzwL.png" data-original="https://i.stack.imgur.com/ldzwL.png" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" /></a>

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

<a href="https://i.stack.imgur.com/xY98M.png" rel="nofollow"><img alt="5 legends" class="b-lazy" data-src="https://i.stack.imgur.com/xY98M.png" data-original="https://i.stack.imgur.com/xY98M.png" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" /></a>

<a href="https://i.stack.imgur.com/KRaf5.png" rel="nofollow"><img alt="4 legends" class="b-lazy" data-src="https://i.stack.imgur.com/KRaf5.png" data-original="https://i.stack.imgur.com/KRaf5.png" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" /></a>

<a href="https://i.stack.imgur.com/dEbRN.png" rel="nofollow"><img alt="3 legends" class="b-lazy" data-src="https://i.stack.imgur.com/dEbRN.png" data-original="https://i.stack.imgur.com/dEbRN.png" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" /></a>

<a href="https://i.stack.imgur.com/NJBGJ.png" rel="nofollow"><img alt="2 legends" class="b-lazy" data-src="https://i.stack.imgur.com/NJBGJ.png" data-original="https://i.stack.imgur.com/NJBGJ.png" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" /></a>

<a href="https://i.stack.imgur.com/6yFjz.png" rel="nofollow"><img alt="1 legend" class="b-lazy" data-src="https://i.stack.imgur.com/6yFjz.png" data-original="https://i.stack.imgur.com/6yFjz.png" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" /></a>

I hope this helps.


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;


  • How can I decode an encoded-word String?
  • Display a numeric keypad on activity without an input area
  • How to deep copy classes with traits mixed in
  • Is it a bad idea to use the comboBox.DisplayMember & ValueMember properties?
  • Reset to the very first commit in Git?
  • Center align a pie chart on svg
  • Controlling volume of running applications in Mac OS X via Objective-C
  • Cache-Control headers, max-age defined but back button always deliver web cache data
  • MemoryCache.Set return removed cache item
  • Combining JSON Arrays
  • selectInput can't populate duplicate values (using uiOutput and renderUI) in Shiny
  • Reading file from C drive from Android Emulator
  • Combine two or more arrays with differnet formant in php
  • request follow redirection without post data
  • Object doesn't support property or method 'valid'
  • general concept-java code and cycle clocks
  • Use object spread operator to construct function object with fields
  • Why is RAM in powers of 2?
  • preg_replace speed optimisation
  • How to notify a specific thread in Java
  • Are there algorithms for putting a digest into the file being digested?
  • How to update powerpivot pivot table filter via cell reference?
  • a concept similar to pointers in as3?
  • Find tangent points on a curve from a user-given point outside the curve
  • LibGdx GLES2.0 cube texel stretching
  • Merge list of objects into consistent list based on common matching attribute in Python
  • How to return a number as a binary string with a set number of bits in python
  • Furthest-point Voronoi diagram in Java
  • android-support-v7-appcompat has same attrs as actionbarsherlock library
  • Counting Treaps
  • custom string delimiters stringtemplate-4
  • Receive mouse move even cursor is outside control
  • Perspective projection, 4 points
  • ZipList with Scalaz
  • C++ Single function pointer for all template instances
  • How to access meteor package name inside package?
  • How to get latest version of a artifact on Bintray using JSONP
  • Tell Git to stop prompting me for conflicts when none really exist?
  • Unable to decode certificate at client new X509Certificate2()
  • Run Powershell script from inside other Powershell script with dynamic redirection to file