2

Is there a way in Vanilla JS to get each line of a text element when the page loads or when the window is resized?

Lets say for the element <h2>This is a beautiful example text</h2>.

On mobile (with less space it breaks into several lines) would be displayed as:

This is a

beautiful

example text

and i would like to get an array like:

["This is a", "beautiful", "example text"]

if we resize the window, maybe on desktop, than the text would have a little bit more space so it needs to break in to less lines, maybe:

This is a beautiful

example text

and then i would like to get:

["This is a beautiful", "example text"]

i have tried something like:

let text = document.querySelector('.text').textContent;
let lines = text.split(/\r|\r\n|\n/);

but i always end up getting the whole string together like ["This is a beautiful text"].

I am trying to do this, because i need to style each line independently (yeap, crazy client wishes!), i cannot change the html and it needs to work when i resize the window. So i think i need to do it with JS, and somehow see how CSS is affecting the text and breaking it into several lines...

so, to clarify, what i am trying to get with JS with the text of each line, when the text breaks because of the container getting smaller. So the Element and the text are the same, but due to less space it breaks into different amount of lines.

Any ideas?

** === UPDATE === ** I can get the number of lines using .getClientRects(). But what i am trying to do is get the text of each of the lines of the element

LuisBento
  • 678
  • 9
  • 16
  • I don't think there will be `\r` or `\n` in between, you will get this as a simple line. – Ace Jul 29 '20 at 06:25
  • yes, that is my problem. i don't know how to get each line separately – LuisBento Jul 29 '20 at 06:28
  • Why not using CSS's `word-break`: https://developer.mozilla.org/en-US/docs/Web/CSS/word-break – adelriosantiago Jul 29 '20 at 06:29
  • Does this answer your question? [Finding number of lines in an html textarea](https://stackoverflow.com/questions/3697096/finding-number-of-lines-in-an-html-textarea) – Flash Thunder Jul 29 '20 at 06:31
  • i can find the number of lines using something like .getClientRect(), but what i am trying is to get the text of each line – LuisBento Jul 29 '20 at 08:06
  • I don't suppose you have the option of using a mono-space font? That would make this all significantly easier (Container width / character width, rounded down to the nearest white-space) – DBS Jul 29 '20 at 09:45
  • that is a good idea, but no, unfortunately i cannot use a monospace font – LuisBento Jul 29 '20 at 09:46
  • Hmm, well I guess you could use JS to step through adding text (word by word) to a hidden container (with the same text styling as the title) and testing it's width compared to the title, though it's rather extreme for just a bit of styling. – DBS Jul 29 '20 at 09:50

2 Answers2

3

I've put together a mock up JS solution.

Overview:

  • Get an array of words
  • Append each word one at a time to a hidden element with the same font-sizing styles as the title
  • Check if the element is larger than the title, if so, add the current line (before we added the last word to it) to an array

function calcLines() {
  // Build an array of each word used in the original title
  var allWords = document.getElementById("title").innerText.match(/\S+/g) || [];
  // The array we will fill with each line
  var lines = [];
  // The current line we are working on building
  var currentLine = "";

  // Work through the words until we're filling the correct amount of space
  for (var i = 0; i < allWords.length; i++) {
    // Build a new line and check if it is now too large for the container
    var newLine = currentLine + allWords[i] + " ";
    document.getElementById("fontSizeTester").innerText = newLine;
    if (
      document.getElementById("fontSizeTester").clientWidth >
      document.getElementById("title").clientWidth
    ) {
      // If the line is now larger, use the previous line (without the last added word) and reset the current line to just the last word
      lines.push(currentLine.trim());
      currentLine = allWords[i] + " ";
    } else {
      // If it's not long enough yet, just keep adding words
      currentLine = newLine;
    }
  }
  // Push any unfinshed final line to the array
  lines.push(currentLine.trim());
  
  console.log(lines);
}

// Run on load and on resize
calcLines();
window.addEventListener("resize", calcLines);
h2 {
  font-size: 30px;
  font-weight: normal;
  font-family: arial;
}

#fontSizeTester {
  position: absolute;
  visibility: hidden;
  height: auto;
  width: auto;
  white-space: nowrap;
}
<h2 id="title">This is a beautiful example text This is a beautiful example text This is a beautiful example text This is a beautiful example text This is a beautiful example text This is a beautiful example text This is a beautiful example text This is a beautiful example text This is a beautiful example text</h2>
<h2 id="fontSizeTester"></h2>
DBS
  • 9,110
  • 4
  • 35
  • 53
0

You can create a flex container in your body , and attach your array there. As result all items in your array will be filling the width of the screen.

https://codepen.io/Vlasenko/pen/vYLwMvE - check it here

const cont = document.querySelector(".container");
      const arr = ["This is a", "beautiful", "example text"];
      arr.forEach((item) => {
        const div = document.createElement("div");
        div.className = "item";
        div.innerText = item;
        cont.appendChild(div);
      });

      <style>
          .container {
            display: flex;
            flex-wrap: wrap;
          }
          .item {
            padding-right: 5px;
          }
        </style>
    <body>
        <div class="container"></div>
    </body>
Anna Vlasenko
  • 916
  • 5
  • 8
  • OP specifically said that changing HTML is off limits. – cyqsimon Jul 29 '20 at 06:52
  • okay, where do you plan to attach your array items? in which element, does that element has a class or id? – Anna Vlasenko Jul 29 '20 at 06:54
  • what i need is almost the other way around. i don't have the array. there is an element on the page, lets say an

    and i need to find how it breaks on the different viewports and i need to get each line individually.

    – LuisBento Jul 29 '20 at 08:05
  • @LuisBento Can you add that `h2` element in your question. Your question is not clear and you are asking something else. – Always Helping Jul 29 '20 at 08:14
  • i have edited the question to make it clearer. So lets say the element is: `

    This is a beautiful example text

    `. On smaller devices, because there is less space it will break into more lines than on a bigger device. I need to get the text for each line.
    – LuisBento Jul 29 '20 at 09:43
  • This is a beautiful example text words from h2 are jumping from line to line if you make screen smaller. I think problem in you app something blocking it – Anna Vlasenko Jul 29 '20 at 09:52