Placing labels in the center of nodes in d3.js

I start with d3.js and try to create a string of nodes, each of which contains a label with a centered number.

I can visually get the desired result, but the way I did it is hardly optimal, as it involves hard-coding the xy coordinates for each text element. Below is the code:

var svg_w = 800; var svg_h = 400; var svg = d3.select("body") .append("svg") .attr("width", svg_w) .attr("weight", svg_h); var dataset = []; for (var i = 0; i < 6; i++) { var datum = 10 + Math.round(Math.random() * 20); dataset.push(datum); } var nodes = svg.append("g") .attr("class", "nodes") .selectAll("circle") .data(dataset) .enter() .append("circle") .attr("class", "node") .attr("cx", function(d, i) { return (i * 70) + 50; }) .attr("cy", svg_h / 2) .attr("r", 20); var labels = svg.append("g") .attr("class", "labels") .selectAll("text") .data(dataset) .enter() .append("text") .attr("dx", function(d, i) { return (i * 70) + 42 }) .attr("dy", svg_h / 2 + 5) .text(function(d) { return d; }); 

The node class is a custom CSS class that I defined separately for circle elements, while the node and labels classes are not explicitly defined and the answer is borrowed from this.

As you can see, the positioning of each text label is hardcoded so that it appears in the center of each node. Obviously, this is the wrong decision.

My question is how should I correctly associate each text label with each circle node dynamically, so if the position of the label changes with the position of the circle automatically. A conceptual explanation is extremely welcome with sample code.

+50
javascript svg
Aug 08 2018-12-12T00:
source share
3 answers

The text-anchor attribute works as expected in the svg element created by D3. However, you need to add text and circle to the regular g element to make sure that text and circle are centered from each other.

To do this, you can change your nodes variable to:

 var nodes = svg.append("g") .attr("class", "nodes") .selectAll("circle") .data(dataset) .enter() // Add one g element for each data node here. .append("g") // Position the g element like the circle element used to be. .attr("transform", function(d, i) { // Set dx and dy here so that other elements can use it. d is // expected to be an object here. dx = i * 70 + 50, dy = svg_h / 2; return "translate(" + dx + "," + dy + ")"; }); 

Note that dataset now a list of objects, so instead of a simple list of strings, you can use dy and dx .

Then replace the circle and text append code as follows:

 // Add a circle element to the previously added g element. nodes.append("circle") .attr("class", "node") .attr("r", 20); // Add a text element to the previously added g element. nodes.append("text") .attr("text-anchor", "middle") .text(function(d) { return d.name; }); 

Now, instead of changing the position of the circle you change the position of the g element, which moves both circle and text .

Here is a JSFiddle showing centered text in circles.

If you want your text to be in a separate g element so that it always appears on top, use the dx and dy values โ€‹โ€‹set in the first g element for transform text.

 var text = svg.append("svg:g").selectAll("g") .data(force.nodes()) .enter().append("svg:g"); text.append("svg:text") .attr("text-anchor", "middle") .text(function(d) { return d.name; }); text.attr("transform", function(d) { return "translate(" + dx + "," + dy + ")"; }); 
+60
Aug 08 2018-12-12T00:
source share

The best answer came from the seeker himself :

just further observation: only with .attr ("text-anchor", "middle") for each text element the label is in the middle horizontally, but slightly from the vertical. I fixed this by adding attr ("y", ".3em") (borrowed from examples on the d3.js website), which seems to work well even for an arbitrary node circle size. However, that it is this additional attribute that eludes my understanding. Of course, this is something for the y-coordinate of each text element, but why is .3em specific? It seems almost magical to me ...

Just add .attr("text-anchor", "middle") to each text element.

Example:

 node.append("text") .attr("x", 0) .attr("dy", ".35em") .attr("text-anchor", "middle") .text(function(d) { return d.name; }); 
+21
Jun 30 '14 at 2:00
source share

This page describes what happens under the svg hood when it comes to text elements. Understanding the basic mechanisms and data structures helped me better understand how I had to modify the code to make it work.

+4
Jul 06 '13 at 1:48
source share



All Articles