1

The goal is to vertically and horizontally align a group of tspan elements.

However, the parent container does not account for dy values.

This causes alignment issues.

If you play with the dy values of this Codepen, the text container height remains the same all the time: https://codepen.io/anon/pen/WLpvrG?editors=1111.

How can you get an accurate bounding rectangle for tspan elements?

<svg id="textBox1" class="textBox" x="0%" y="0%" width="100%" height="25%">
  <rect class="background" x="0%" y="0%" width="100%" height="100%" fill="gray" fill-opacity="0.5" />
  <text class="tspanGroup" x="0" y="0"><tspan x="50%" dy="0em" text-anchor="middle">tspan line 0</tspan><tspan x="50%" dy="1.5em" text-anchor="middle">tspan line 1</tspan><tspan x="50%" dy="1.5em" text-anchor="middle">tspan line 2</tspan></text>
</svg>
Crashalot
  • 33,605
  • 61
  • 269
  • 439
  • The text contiainer height is adepting just fine. It just doesn't adept when you change the `dy` of the _first_ `tspan` element, because you're essentially just moving it down instead of changing its height. Keep in mind that the `dy` attribute indicates a shift along y-axis. It doesn't indicate a resize. A simple workaround would be to just add an invisible `tspan` element as the first one (don't give it any content) and work with the second one. – icecub Dec 21 '18 at 04:45
  • @icecub can you post as an answer to get credit? thanks. – Crashalot Dec 21 '18 at 04:50
  • @icecub also do you recommend a better way to vertically/horizontally center a text element? thanks for your help! – Crashalot Dec 21 '18 at 04:50

1 Answers1

1

The dy attribute indicates a shift along the y-axis. It doesn't indicate a resize. So if you change that value on your first <tspan> element, you're just moving it up or down. As the container wraps around the elements, it doesn't change size when you simply just move them.

If you want to center the text both vertically and horizontally, I suggest you take a look at the second answer here: How to place and center text in an SVG rectangle. I don't really see a point in copy / pasting it, haha.

Alright, took me a moment to get it to work, but here you go:

// Wait for document to load so the elements don't return undefined
document.addEventListener("DOMContentLoaded", function(event) {
 // Center the text
 centerText();

 setTimeout(function() {
  // Change font sizes
  document.getElementById('size1').style.fontSize = "12px";
  document.getElementById('size2').style.fontSize = "16px";
  document.getElementById('size3').style.fontSize = "20px";

  // Center the text again
  centerText();
 }, 3000);
});

function centerText(){
 // Get the elements
 const container = document.getElementById('textBox1');
 const textEle = document.getElementById('txt');

 // User getBBox for SVG element data
 const cBox = container.getBBox();
 const tBox = textEle.getBBox();

 // Get width / height of container SVG
 const contHeight = cBox.height;
 const contWidth = cBox.width;

 // Get width / height of TEXT element
 const txtHeight = tBox.height;
 const txtWidth = tBox.width;

 // Set the attributions correctly to center text
 textEle.setAttribute("x", (contWidth/2)-(txtWidth/2));
 textEle.setAttribute("y", (contHeight/2)-(txtHeight/2));
}
<svg id="rootBox" width="375" height="812" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
 <svg id="textBox1" class="textBox" x="0" y="0" width="100%" height="25%">
  <rect class="background" x="0%" y="0%" width="100%" height="100%" fill="gray" fill-opacity="0.5" />
  <text class="tspanGroup" x="50%" y="50%" dominant-baseline="middle" text-anchor="middle" id="txt">
   <tspan x="50%" dy="0em" id="size1">tspan line 0</tspan>
   <tspan x="50%" dy="1.5em" id="size2">tspan line 1</tspan>
   <tspan x="50%" dy="1.5em" id="size3">tspan line 2</tspan>
  </text>
 </svg>
</svg>
icecub
  • 8,615
  • 6
  • 41
  • 70
  • thanks! but why does changing the second and third dy values affect the text height? – Crashalot Dec 21 '18 at 05:07
  • @Crashalot Because the first one stays in place if you do that. It's like you have 3 lines. If you move the first down, it remains 3 lines. But if you move the second or third, it becomes 4, 5 etc lines. Just an empty in between. That makes it bigger. – icecub Dec 21 '18 at 05:08
  • @Crashalot As a tip, I would suggest that you use your browser's "Inspect Element" feature. Just right click on it and inspect it. Then, when you hover the code in the inspector, it shows you the size of the element by drawing lines in your screen. That way it's very easy to see exactly what's happening. An absolute must-have for front-end design work if you ask me. – icecub Dec 21 '18 at 05:21
  • thanks! also any clue why these lines won't center? https://codepen.io/anon/pen/royxNx?editors=1010 – Crashalot Dec 21 '18 at 06:26
  • @Crashalot Ye that's because you're using the `tspan` elements. The center point becomes the beginning of the first line because of that. I think you would have to use JS to calculate the width and height of the `` element, then set the `x` attribute to `calc(50% - half width of text)` and the `y` to `calc(50% - half height of text)`. – icecub Dec 21 '18 at 06:40
  • thanks, but the problem is each tpsan height is the same as the whole group when you use javascript. do you know how to get the proper height of the text (even when you check from google chrome)? – Crashalot Dec 21 '18 at 06:46
  • @Crashalot Give me some time, I'll work on a working example and edit my answer for you. Might take a few minutes, have to properly test it of course. – icecub Dec 21 '18 at 06:48
  • @Crashalot Alright, I've edited my answer. There's an example in there now. – icecub Dec 21 '18 at 07:26
  • thanks so much! we tried this already, but it doesn't work when the font sizes changes (which we need to let users do): https://codepen.io/anon/pen/NepNjb?editors=1111. there's the code pen where you can play with the values. – Crashalot Dec 21 '18 at 07:36
  • @Crashalot That's because you need to re-calculate on change. Just make an event handler for when the user changes the font size and run this code in a function for the event handler. Problem solved :) – icecub Dec 21 '18 at 07:38
  • thanks! if you check the JS, the centering is occurring after the font size change already. did it work for you somehow? – Crashalot Dec 21 '18 at 07:41
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/185596/discussion-between-icecub-and-crashalot). – icecub Dec 21 '18 at 07:45