How can I dynamically resize an SVG rect based on text width?
I would use getComputedTextLength
to measure the text. I don't know if there is an equivalent for this in D3.js My answer is using plain javascript and is assuming that the rect and the text center is {x:50,y:25 } and you are using text{dominant-baseline:middle;text-anchor:middle;}
let text_length = txt.getComputedTextLength();
rct.setAttributeNS(null,"width",text_length )
rct.setAttributeNS(null,"x",(50 - text_length/2) )
svg{border:1px solid}
text{dominant-baseline:middle;text-anchor:middle;}
<svg viewBox="0 0 100 50">
<rect x="25" y="12.5" width="50" height="25" stroke="black" fill="none" id="rct" />
<text x="50" y="25" id="txt">Test text</text>
</svg>
Alternatively instead of txt.getComputedTextLength()
you may use txt.textLength.baseVal.value
Normally I would comment, but I don't have enough reputation points.
The accepted answer has the right idea, but it doesn't work, how he coded it. The first problem is, he uses an arrow function instead of an anonymus function. In arrow functions, this
has a different scope. So use an anonymus function here.
The second problem is the order of rect
and text
, as you can see in the source code, in the question. Since rect
is appended before text
, the parent node doesn't have the child text
yet. So you have to just append
the rect
, then append the text
and set its attrs
and then set the attrs
of rect
. So the solution is:
var rects = nodeEnter.append("rect")
var nodeText = nodeEnter.append("text")
.attr("class", "node-text")
.attr("y", rectH / 2)
.attr("dy", ".35em")
.text(function (d) {
return d.data.name;
});
nodeText // The bounding box is valid not before the node addition happened actually.
.attr("x", function (d) {
return (rectW - this.getBBox().width) / 2;
});
rect
.attr('width', function () {
return this.parentNode.childNodes[1].getComputedTextLength();
})
.attr("height", rectH);
Note: If you don't need the parameter d
, you don't have to accept it, like I did.
The solution is pretty simple when you remember that the this
binding in the attr()
call refers to the associated HTML (SVG) element:
rects.attr("width",
d => this.parentNode.childNodes[1].getComputedTextLength() + 20
);
The rect
is the first element in a list of SVG elements that make up the displayed node. The text
for that node is at index 1 (as follows from the append
calls).