Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper way to change a SVG <text> without changing its children <tspan>

Consider this simple SVG with a <text> element containing some <tspan>s:

<svg>
  <text x="10" y="30">
    Foo Bar Baz
    <tspan x="30" dy="30">FooBar</tspan>
    <tspan x="30" dy="30">FooBaz</tspan>
  </text>
</svg>

Now suppose we want to change the value of the text itself (in this case, "Foo Bar Baz") while keeping its <tspan> descendants. If we use textContent, innerHTML (modern browsers support innerHTML for SVG) etc that will replace everything, children included:

document.querySelector("text").textContent = "New Foo";
<svg>
  <text x="10" y="30">
    Foo Bar Baz
    <tspan x="30" dy="30">FooBar</tspan>
    <tspan x="30" dy="30">FooBaz</tspan>
  </text>
</svg>

One workaround is getting the first NodeList element of the child nodes, which is normally the text itself:

document.querySelector("text").childNodes[0].textContent = "New Foo";
<svg>
  <text x="10" y="30">
    Foo Bar Baz
    <tspan x="30" dy="30">FooBar</tspan>
    <tspan x="30" dy="30">FooBaz</tspan>
  </text>
</svg>

However, this workaround is quite hacky. "Why?", you might ask... well, using an hardcoded index in a collection is hacky enough to me. Also, like I wrote above, the first element in the collection is normally the text itself, but I have no idea if this is implementation-dependent and, therefore, a new browser can simply change that order in the collection, implementing a different sequence.

Hence the proper way in my question's title: is there a property or any other method to specifically change the text content only in an SVG <text> element?

like image 866
Megaptera novaeangliae Avatar asked Dec 30 '22 13:12

Megaptera novaeangliae


1 Answers

As I see it you have two options. 1) Building on your last example where you look for the text node. You know that there is one text node with content. You can test all the childNodes of <text> to see if it is a text node and then replace that node. 2) You turn the text node into a <tspan> element just like the other child elements. This will make no difference visually and at then same time it is easier to refer to the element and style it if needed. The "cost" for adding this element is minimal.

document.querySelector("text > .first").textContent = "New Foo";
<svg>
  <text x="10" y="30">
    <tspan class="first">Foo Bar Baz</tspan>
    <tspan x="30" dy="30">FooBar</tspan>
    <tspan x="30" dy="30">FooBaz</tspan>
  </text>
</svg>
like image 173
chrwahl Avatar answered Jan 14 '23 03:01

chrwahl