Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

393
Views
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?

over 4 years ago · Santiago Trujillo
5 answers
Answer question

0

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>

over 4 years ago · Santiago Trujillo Report

0

You might use the nodeType property in a loop. It is important to notice that other text nodes can be added by the parser, so you also have to choose which one you need. A simple implementation substitutes the first node with nodeType===3. Other implementations are possible, taking into account that other type 3 nodes will have simple texts like \n. This will substitute the first text (not tspan) node that has some letter:

const r = document.querySelector("text");
let hasText = /\w/;
for (x = 0; x < r.childNodes.length; x++){
  if (r.childNodes[x].nodeType === 3 && hasText.test(r.childNodes[x].textContent)){
    r.childNodes[x].textContent = "New Foo";
    break;
  }
}
<svg>
  <text x="10" y="30">
    <tspan x="30" dy="30">FooBar</tspan>
    <tspan x="30" dy="30">FooBaz</tspan>
    Foo Bar Baz
  </text>
</svg>

over 4 years ago · Santiago Trujillo Report

0

This same approach how vqf did with some did improvements.

const element = document.querySelector('text');

function changeText(text, node) {
  for (const n of node.childNodes) {
    let string = n.textContent.trim();
    if (!n.hasChildNodes() && string !== '') {
      n.textContent = text;
    }
  }
}

changeText('New Foo Baz', element);
<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>

The second way just clean up first line and append new text using insertAdjacentText which gives more flexible approach.

const element = document.querySelector('text');

function changeText(text, node) {
  for (const n of node.childNodes) {
    let string = n.textContent.trim();
    if (!n.hasChildNodes() && string !== '') {
      n.textContent = '';
    }
  }

  node.insertAdjacentText('afterbegin', text);
  node.insertAdjacentText('beforeend', text);
}

changeText('New Foo Baz', element);
<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>

over 4 years ago · Santiago Trujillo Report

0

A different approach from the others. Replace the Element.children nodes directly using Element.replaceChildren() (No compatible with IE):

const textNode = document.querySelector('text');

function updateTextContent(node, text) {
  node.replaceChildren(text, ...node.children);
}

updateTextContent(textNode, '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>

If you need support for IE, replace the textContent property value, and then use append to add the children nodes:

function updateTextContent(node, text) {
  const children = [...node.children];
  node.textContent = "";
  node.append(text, ...children);
}
over 4 years ago · Santiago Trujillo Report

0

"Is there a property or any other method to specifically change the text content only?"

No, probably not. In fact, I think it might be for the better that such a method doesn't exist. Say for example that your <text> node looked like this:

<text>
  Foo
  <tspan>Bar</tspan>
  Baz
</text>

And now we would call the unknown method to change the text into Hello world. What would the desired outcome be?

<text>
  Hello world
  <tspan>Bar</tspan>
</text>
<text>
  <tspan>Bar</tspan>
  Hello world
</text>

or even

<text>
  Hello
  <tspan>Bar</tspan>
  world
</text>

Looking at it like this, it becomes clear that the "text content" can't be seen as a special case that exists apart from the node tree: just like the <tspan> in your example, there can be multiple text nodes in any order and quantity. It's therefore hard to justify a special method that only changes text nodes, without introducing method to only change all the <tspan>, <div> and so on as well.

Solutions

If you regard all the text nodes as some kind of typeless elements like this:

<text>
  <text>Foo</text>
  <tspan>Bar</tspan>
  <text>Baz</text>
</text>

there is little question about how you would approach targeting and changing your desired element. Options include:

  • Grab it by index (like you already did). Judging from the fictional snippet above, it would be pretty safe to assume that Foo will indeed always be the first element. But just like with the :first selector in CSS, you might rather not want to depend so rigidly on a specific structure.
  • A class="text" attribute would be very clear, but then you'd need to wrap your text in an actual element (which might be the best solution, but I'm following your specs here).
  • Grab the first element that is of the right type, just like you would with any other element.

The snippet below would therefore be a perfectly valid way to alter the first text node in a list of children (written in a functional manner to keep it concise).

Array.from(document.querySelector("text").childNodes)
  .find(e => e.nodeType === Node.TEXT_NODE)
  .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>

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!