henry henry - 2 months ago 6
HTML Question

After creating a <text> element with javascript, why can't I getBBox?

I have a script that adds a

<text>
element to my markup, inside an existing
<svg>
. Running
getBBox()
on that new element gives me an error. If I include the
<text>
in the markup to begin with and run an equivalent script, getBBox runs without any problems. Is the DOM not fully treating my js-built
<text>
as a text element… am I missing some "the thing written '<text>' is in fact a
<text>
" step?



function works() {
console.log('start "works"')
var svgElem = document.querySelector('.works');
var textElem = svgElem.querySelector('text');
var textBBox = textElem.getBBox();
console.log(textBBox);
console.log('end "works"')
}
works();

function doesntwork() {
console.log('start "doesntwork"')
var svgElem = document.querySelector('.doesntwork');
var textElem = document.createElement("text");
textElem.appendChild(svgElem.firstChild);
svgElem.appendChild(textElem);
console.log('"doesntwork" breaks after this');
var textBBox = textElem.getBBox(); // breaks the script
console.log(textBBox);
console.log('end "doesntwork"')
}
doesntwork();

<svg class="doesntwork">
not working
</svg>

<svg class="works">
<text>
working
</text>
</svg>








Less generic second part:

In my full project, I'm actually turning

<div class="target">content</div>


into

<div class="target"><svg><text>content</text></svg></div>


using js to create a
<text>
and an
<svg>
. The idea is basically

var targetElems = document.querySelectorAll('.target');
for (var i = 0; i < targetElems.length; ++i) { // for each target
var targetElem = targetElems[i];
var textElem = document.createElement("text"); // build a <text>
while (targetElem.firstChild) // put the target's content (which could include child elements) in the <text>
textElem.appendChild(targetElem.firstChild);
var svgElem = document.createElement("svg"); // build an <svg>
svgElem.appendChild(textElem); // put the <text> in the <svg>
targetElem.appendChild(svgElem); // put the <svg> in the target
var textBBox = textElem.getBBox(); // want to be able to get the <text>'s BBox (this currently has a breaking error)
console.log(textBBox);
}


Do I have to add a signal at each step - "this is a
<text>
, this is an
<svg>
"?

Or am I going about the whole thing wrong / is there some smarter way to turn
.target > [content]
into
.target > svg > text > [content]
?

Answer

You should be able to use: createElementNS.

document.createElementNS(String namespaceURI, String qualifiedName).

Where:

namespaceURI = "http://www.w3.org/2000/svg"
name = "text"

Something like this:

function worksnow() {
  console.log('start "worksnow"')
  var svgElem = document.querySelector('.worksnow');
  var textElem = document.createElementNS("http://www.w3.org/2000/svg", "text"); // CHANGED THIS
  textElem.appendChild(svgElem.firstChild);
  svgElem.appendChild(textElem);
  var textBBox = textElem.getBBox(); // no longer breaks the script!
  console.info(textBBox); // Gets SVGRect data.
  console.log('end "worksnow"')
}
worksnow();
<svg class="worksnow">
  works now!
</svg>

So, in the console, you'll get this:

SVG