Can I measure a text element?

Let’s say I create a new text element with:

const label = g
  .append("svg:text")
  .attr("class", "name")
  .attr("ref", "name")
  .attr("id", "name")
  .attr("dx", function (d) {
    return outerNodebbox.width / 3;
  })
  .attr("dy", function (d) {
    return outerNodebbox.height / 3;
  })
  .attr("dominant-baseline", "central")
  .text((d) => {
    return d["name"];
  });

It is my understanding, based on the SO Question SVG get text element width, that I can get the bounding box of the svgtext element by calling label.getBBox().

However, when I try to do that I get the error getBBox is not a function.

I believe that label is not the svg text element itself, but a D3 object. Is that correct? If so, how can I get the SVG element?

Should I expect to be able to measure the size of that text element?

1 Like

If everything is set up correctly, then I think that label should actually be a string since the last item in your chain is

.text((d) => {
  return d["name"];
})

and that should return the contents of d.name, which typically is a string. Of course, this assumes that g is an SVG group with bound data. Otherwise, d would be undefined. If you want label to reference the D3 object, you could do

const label = g
  .append("svg:text");
label
  .text((d) => {
    return d["name"];
  })

If you want to refer to the SVG element itself (to access SVG element methods, for example), you could reference label.node().

Having said that, getBBox is a little tricky since it won’t work until after the element is laid out on the page. There’s good reason for that, since the layout process influences the bounding box and many other element attributes. The standard trick is to lay out the SVG elements, read any attributes you need, and only then modify it all to the state that you want.

Here’s one approach to doing that:

Based on what I am seeing, I can do the following:

const svg = d3.select("#chart");

let composite = svg
  .append("svg:g")
  .selectAll("g")
  .data(nodes, (d) => d.id);

const g = composite.enter();

const label = g
  .append("svg:text")
  .attr("class", "name")
  .attr("ref", "name")
  .attr("id", "name")
  .attr("dominant-baseline", "middle")
  .attr("font-size", "10px")
  .text((d) => {
    return d["name"];
  });

let labelBBox = label.node().getBBox();
console.log("label size", labelBBox.width, labelBBox.height);

As near as I can tell, the svg element is added immediately and I can get the bounding box to measure the size of the label.

Is there some reason to think this won’t work in general?

A complete CodePen is at https://codepen.io/ericg_off/pen/LYaMQMR where I am using this technique to layout a composite node which is as wide as it needs to be to hold the string content.

I based this work off of a old article at