'How to align separate svg text elements like character alignment in word

I am working with an svg text element and I need to create separate <text></text> element per character of a particular word, Lorem in this case.

For example,

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

const svgns = "http://www.w3.org/2000/svg"

//vboxDim

var vboxW = 200;
var vboxH = 200;

//assigning svg element attribute 
svg.setAttribute('class', 'layer1');
svg.setAttribute('xmlns', svgns);
svg.setAttribute('viewBox', `0 0 ${vboxW} ${vboxH}`);

//make background
var fill1 = '#F1C40F';

let bg = document.createElementNS(svgns, 'rect');
bg.setAttribute('class', 'bg');
bg.setAttribute('id', 'bg');
bg.setAttribute("width", `${vboxW}`);
bg.setAttribute("height", `${vboxH}`);
bg.setAttribute("fill", fill1);
svg.appendChild(bg);

var txtArray1 = ['L', 'o', 'r', 'e', 'm'];


var textX = svg.viewBox.baseVal.width / 2;
var textY = svg.viewBox.baseVal.height / 2;


for (var i = 0; i < txtArray1.length; i++) {
    let text1 = document.createElementNS(svgns, "text")
    text1.setAttribute('class', 't0' + i)
    text1.setAttribute('id', 't0' + i);
    text1.setAttribute('x', textX);
    text1.setAttribute('y', textY);
    text1.setAttribute('fill','green');
    text1.setAttribute('dominant-baseline', 'middle'); //positions text in the middle
    text1.setAttribute('text-anchor', 'middle'); //positions text in the middle
    text1.textContent = txtArray1[i];
    svg.appendChild(text1);
    var el = document.getElementById(`t0${i}`);
    var width = el.getBBox().width;
    textX = textX + width;

}

let text2 = document.createElementNS(svgns, "text")
text2.setAttribute('class', 'word')
text2.setAttribute('id', 'word');
text2.setAttribute('x', (svg.viewBox.baseVal.width / 2));
text2.setAttribute('y', (svg.viewBox.baseVal.height / 2) - 50);
text2.setAttribute('fill','brown');
text2.setAttribute('dominant-baseline', 'middle'); //positions text in the middle
text2.setAttribute('text-anchor', 'middle'); //positions text in the middle
text2.textContent = 'Lorem';
svg.appendChild(text2);
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style></style>

</head>

<body>

    <svg>
        <script href="index.js"></script>
    </svg>
</body>

</html>

I want these individual characters to be aligned if they were created as a word

class="word"

While designing, my initial idea was to get the BBox().width of the immediately created character and increase the x of the next element by the same value. But the final element is nowhere closer to element in class="word".

What is the best strategy to create individual text elements and yet align them (green color) as they would appear in a sentence (brown color) ?

Update I have tried what @Robert Longson suggested. I am not sure if it was done wrongly, but it is not accurate.

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

const svgns = "http://www.w3.org/2000/svg"

//vboxDim
var vboxW = 200;
var vboxH = 200;

//assigning svg element attribute 
svg.setAttribute('class', 'layer1');
svg.setAttribute('xmlns', svgns);
svg.setAttribute('viewBox', `0 0 ${vboxW} ${vboxH}`);

//make background
var fill1 = '#F1C40F';

let bg = document.createElementNS(svgns, 'rect');
bg.setAttribute('class', 'bg');
bg.setAttribute('id', 'bg');
bg.setAttribute("width", `${vboxW}`);
bg.setAttribute("height", `${vboxH}`);
bg.setAttribute("fill", fill1);

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

var txtArray1 = ['L', 'o', 'r', 'e', 'm'];


var textX = svg.viewBox.baseVal.width / 2;
var textY = svg.viewBox.baseVal.height / 2;



let text2 = document.createElementNS(svgns, "text")
text2.setAttribute('class', 'word')
text2.setAttribute('id', 'word');
text2.setAttribute('x', (svg.viewBox.baseVal.width / 2));
text2.setAttribute('y', (svg.viewBox.baseVal.height / 2) - 50);
text2.setAttribute('fill','brown');
text2.setAttribute('dominant-baseline', 'middle'); //positions text in the middle
text2.setAttribute('text-anchor', 'middle'); //positions text in the middle
text2.textContent = 'Lorem';
svg.appendChild(text2);



for (var i = 0; i < txtArray1.length; i++) {
    var x = document.getElementById('word');
    let text1 = document.createElementNS(svgns, "text")
    text1.setAttribute('class', 't0' + i)
    text1.setAttribute('id', 't0' + i);
    text1.setAttribute('x', x.getStartPositionOfChar(i).x);
    text1.setAttribute('y', x.getStartPositionOfChar(i).y);
    text1.setAttribute('fill','green');
    text1.setAttribute('dominant-baseline', 'middle'); //positions text in the middle
    text1.setAttribute('text-anchor', 'middle'); //positions text in the middle
    text1.textContent = txtArray1[i];
    svg.appendChild(text1);


}

text2.setAttribute('y', 75);
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style></style>

</head>

<body>
    <link rel="stylesheet" href="style.css">
    </link>
    <svg>
        <script href="index.js"></script>
    </svg>
</body>

</html>


Solution 1:[1]

  • Call getStartPositionOfChar to find out where each character is.

  • When you make the copy you don't need to set text-anchor or dominant-baseline because the original character positions already embody that.

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

const svgns = "http://www.w3.org/2000/svg"

//vboxDim
var vboxW = 200;
var vboxH = 200;

//assigning svg element attribute 
svg.setAttribute('class', 'layer1');
svg.setAttribute('xmlns', svgns);
svg.setAttribute('viewBox', `0 0 ${vboxW} ${vboxH}`);

//make background
var fill1 = '#F1C40F';

let bg = document.createElementNS(svgns, 'rect');
bg.setAttribute('class', 'bg');
bg.setAttribute('id', 'bg');
bg.setAttribute("width", `${vboxW}`);
bg.setAttribute("height", `${vboxH}`);
bg.setAttribute("fill", fill1);

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

var txtArray1 = ['L', 'o', 'r', 'e', 'm'];


var textX = svg.viewBox.baseVal.width / 2;
var textY = svg.viewBox.baseVal.height / 2;



let text2 = document.createElementNS(svgns, "text")
text2.setAttribute('class', 'word')
text2.setAttribute('id', 'word');
text2.setAttribute('x', (svg.viewBox.baseVal.width / 2));
text2.setAttribute('y', (svg.viewBox.baseVal.height / 2) - 50);
text2.setAttribute('fill','brown');
text2.setAttribute('dominant-baseline', 'middle'); //positions text in the middle
text2.setAttribute('text-anchor', 'middle'); //positions text in the middle
text2.textContent = 'Lorem';
svg.appendChild(text2);



for (var i = 0; i < txtArray1.length; i++) {
    var x = document.getElementById('word');
    let text1 = document.createElementNS(svgns, "text")
    text1.setAttribute('class', 't0' + i)
    text1.setAttribute('id', 't0' + i);
    text1.setAttribute('x', x.getStartPositionOfChar(i).x);
    text1.setAttribute('y', x.getStartPositionOfChar(i).y);
    text1.setAttribute('fill','green');
    text1.textContent = txtArray1[i];
    svg.appendChild(text1);


}

text2.setAttribute('y', 75);
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style></style>

</head>

<body>
    <link rel="stylesheet" href="style.css">
    </link>
    <svg>
        <script href="index.js"></script>
    </svg>
</body>

</html>

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 Robert Longson