'How to place equally spaced svg text in order

I have a svg element generated with javascript, that looks like this

 <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720">   
<rect />
<text >
    <tspan >this is line 1 test 1</tspan>
</text>
<text >
    <tspan>this is line 2 test 2</tspan>
</text>
<text>
    <tspan>this is line 3 test 3</tspan>
</text>
</svg>

I am trying to place each tspan in the equal distance for asethetics. Currently I am doing this with the following js

// select svg
const svg = document.querySelector("svg");

// variable for the namespace 
const svgns = "http://www.w3.org/2000/svg"

//make background
var fill1 = "#e6e6e6";

let bg = document.createElementNS(svgns, 'rect');
bg.setAttribute("class", "bg");
bg.setAttribute("id", "bg");
bg.setAttribute("width", "1280");
bg.setAttribute("height", "720");
bg.setAttribute("fill", fill1);

// append the new rectangle to the svg
svg.appendChild(bg);

/*line1 start */
var size = 15; //change only for the lines
var content1 = 'this is line 1 test 1'; //change as per the lines


var text1 = document.createElementNS(svgns, "text")
text1.setAttribute("class", "t1")
text1.setAttribute("id", "t1");
text1.setAttribute("font-size", size.toString());
text1.setAttribute("font-family", "Monospace");


var constX = 10.23;
var constY = 135.05;

var tspan1 = document.createElementNS(svgns, "tspan");
tspan1.setAttribute("class", "tsp1");
tspan1.setAttribute("id", "tsp1");
tspan1.setAttribute("x", constX.toString());
tspan1.setAttribute("y", constY.toString());
tspan1.textContent = content1;
text1.appendChild(tspan1);

svg.appendChild(text1);
/*line1 finish */

/*line2 start */
var size = 15; //change only for the lines
var content1 = 'this is line 2 test 2'; //change as per the lines

var text1 = document.createElementNS(svgns, "text")
text1.setAttribute("class", "t2")
text1.setAttribute("id", "t2");
text1.setAttribute("font-size", size.toString());
text1.setAttribute("font-family", "Monospace");

var constX = 10.23;
var el = document.getElementById('tsp1');
var constY = el.getExtentOfChar(0).y;

var tspan1 = document.createElementNS(svgns, "tspan");
tspan1.setAttribute("class", "tsp2");
tspan1.setAttribute("id", "tsp2");
tspan1.setAttribute("x", constX.toString());
tspan1.setAttribute("y", constY.toString());
tspan1.textContent = content1;
text1.appendChild(tspan1);

svg.appendChild(text1);
/*line2 finish */

/*line3 start */
var size = 15; //change only for the lines
var content1 = 'this is line 3 test 3'; //change as per the lines

var text1 = document.createElementNS(svgns, "text")
text1.setAttribute("class", "t3")
text1.setAttribute("id", "t3");
text1.setAttribute("font-size", size.toString());
text1.setAttribute("font-family", "Monospace");

var constX = 10.23;
var el = document.getElementById('tsp2');
var constY = el.getExtentOfChar(0).y;

var tspan1 = document.createElementNS(svgns, "tspan");
tspan1.setAttribute("class", "tsp3");
tspan1.setAttribute("id", "tsp3");
tspan1.setAttribute("x", constX.toString());
tspan1.setAttribute("y", constY.toString());
tspan1.textContent = content1;
text1.appendChild(tspan1);

svg.appendChild(text1);
/*line3 finish */
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720">
    </svg>

But the order of these lines are reversed. How can I achieve equal spacing with the correct order?

The following piece is generating the y of the tspan

  var el = document.getElementById('tsp2');
  var constY = el.getExtentOfChar(0).y;


Solution 1:[1]

Well this is kind of a weird way to place SVG text - normally you just specify x and y on the text element - but I'm sure you have your reasons.

The problem here is that you're using the y attribute returned from getExtentOfChar - which is giving you the top of the bounding box of the previous tspan and using that as your y for the next tspan. But the y for a tspan sets the BASELINE position, not the bounding box top - so that tspan is being painted above the previous one. You can fix this by adding the height attribute (or for better spacing - 2x the height attribute returned from getExtentOfChar) - like so.

// select svg
const svg = document.querySelector("svg");

// variable for the namespace 
const svgns = "http://www.w3.org/2000/svg"

//make background
var fill1 = "#e6e6e6";

let bg = document.createElementNS(svgns, 'rect');
bg.setAttribute("class", "bg");
bg.setAttribute("id", "bg");
bg.setAttribute("width", "1280");
bg.setAttribute("height", "720");
bg.setAttribute("fill", fill1);

// append the new rectangle to the svg
svg.appendChild(bg);

/*line1 start */
var size = 15; //change only for the lines
var content1 = 'this is line 1 test 1'; //change as per the lines


var text1 = document.createElementNS(svgns, "text")
text1.setAttribute("class", "t1")
text1.setAttribute("id", "t1");
text1.setAttribute("font-size", size.toString());
text1.setAttribute("font-family", "Monospace");


var constX = 10.23;
var constY = 135.05;

var tspan1 = document.createElementNS(svgns, "tspan");
tspan1.setAttribute("class", "tsp1");
tspan1.setAttribute("id", "tsp1");
tspan1.setAttribute("x", constX.toString());
tspan1.setAttribute("y", constY.toString());
tspan1.textContent = content1;
text1.appendChild(tspan1);

svg.appendChild(text1);
/*line1 finish */

/*line2 start */
var size = 15; //change only for the lines
var content1 = 'this is line 2 test 2'; //change as per the lines

var text1 = document.createElementNS(svgns, "text")
text1.setAttribute("class", "t2")
text1.setAttribute("id", "t2");
text1.setAttribute("font-size", size.toString());
text1.setAttribute("font-family", "Monospace");

var constX = 10.23;
var el = document.getElementById('tsp1');
var constY = el.getExtentOfChar(0).y + 2 * el.getExtentOfChar(0).height;

console.log(constY);

var tspan1 = document.createElementNS(svgns, "tspan");
tspan1.setAttribute("class", "tsp2");
tspan1.setAttribute("id", "tsp2");
tspan1.setAttribute("x", constX.toString());
tspan1.setAttribute("y", constY.toString());
tspan1.textContent = content1;
text1.appendChild(tspan1);

svg.appendChild(text1);
/*line2 finish */

/*line3 start */
var size = 15; //change only for the lines
var content1 = 'this is line 3 test 3'; //change as per the lines

var text1 = document.createElementNS(svgns, "text")
text1.setAttribute("class", "t3")
text1.setAttribute("id", "t3");
text1.setAttribute("font-size", size.toString());
text1.setAttribute("font-family", "Monospace");

var constX = 10.23;
var el = document.getElementById('tsp2');
var constY = el.getExtentOfChar(0).y + 2 * el.getExtentOfChar(0).height;
console.log(constY);

var tspan1 = document.createElementNS(svgns, "tspan");
tspan1.setAttribute("class", "tsp3");
tspan1.setAttribute("id", "tsp3");
tspan1.setAttribute("x", constX.toString());
tspan1.setAttribute("y", constY.toString());
tspan1.textContent = content1;
text1.appendChild(tspan1);

svg.appendChild(text1);
/*line3 finish */
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720">
    </svg>

Solution 2:[2]

or dump all svga JavaScript (says the person who always includes JS in his answers)

and do it with SVG keyPoints?

<svg viewBox="0 0 200 50">
  <path id="vline" fill="none" stroke="red" d="m10,0v50"></path>
  <text>this is line 1
    <animateMotion dur="0.001s" fill="freeze" keyPoints="0;0.25" keyTimes="0;1" calcMode="linear">
      <mpath href="#vline"></mpath>
    </animateMotion>
  </text>
  <text>this is line 2
    <animateMotion dur="0.001s" fill="freeze" keyPoints="0;.5" keyTimes="0;1" calcMode="linear">
      <mpath href="#vline"></mpath>
    </animateMotion>
  </text>
  <text>this is line 3
    <animateMotion dur="0.001s" fill="freeze" keyPoints="0;.75" keyTimes="0;1"calcMode="linear">
      <mpath href="#vline"></mpath>
    </animateMotion>
  </text>
</svg>

and add https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/dominant-baseline for better alignment

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Michael Mullany
Solution 2