3

I know that it's possible to split a string based on its length by number of characters. But how can I split an html string based on pixels without cutting words off?

For example :

myString = "Lorem Ipsum has been the industry's standard dummy text ever since the 1500s";
splitThroughPixel(myString, 100) // Shall return something like ["Lorem Ipsum has", "been the industry's", "dummy text", "since the", "1500s"] (not the true splitting, just to give an idea)

splitThroughPixel(myString, 100) shall split myString into string pieces of 100px max each (without cutting words).

How can I achieve that ?

I'm already able to get the full pixel length of a string using this javascript method (if it can ever help) :

function getWidth(pText, pFontSize, pStyle) {
    var lDiv = document.createElement('div');

    document.body.appendChild(lDiv);

    if (pStyle != null) {
        lDiv.style = pStyle;
    }
    lDiv.style.fontSize = "" + pFontSize + "px";
    lDiv.style.position = "absolute";
    lDiv.style.left = -1000;
    lDiv.style.top = -1000;

    lDiv.innerHTML = pText;

    document.body.removeChild(lDiv);
    lDiv = null;

    return lDiv.clientWidth;
}

For example : getWidth(myString ) return 510 (which is the number of pixel on the screen of the string myString)

Thanks for taking time for helping me.

kabrice
  • 1,475
  • 6
  • 27
  • 51
  • I really did not get your question, can you tell me more simple way – Dipak Feb 07 '18 at 17:10
  • The fact is we used to split string based on the number of character. I want split string based on pixels (not character). Please is it clear ? – kabrice Feb 07 '18 at 17:15
  • What are you using this method for? – kamoroso94 Feb 07 '18 at 17:21
  • Possible duplicate of [Determine Pixel Length of String in Javascript/jQuery?](https://stackoverflow.com/questions/2057682/determine-pixel-length-of-string-in-javascript-jquery) – Heretic Monkey Feb 07 '18 at 17:22
  • @MikeMcCaughan it is not a dupe. Your post is about determining pixel, mine is about Splitting through pixel ! – kabrice Feb 07 '18 at 17:59
  • Determining the number of pixels in a string is the hard part, splitting is the easy part; you just keep adding characters until you hit the number of pixels. – Heretic Monkey Feb 07 '18 at 19:20

3 Answers3

2

First of all, I made some corrections to your getWidth function because you return lDiv.clientWidth but you set lDiv to null right before, so it's going to throw an error. Therefore, I stored the .clientWidth into a variable, then returned it:

function getWidth(pText, pFontSize, pStyle) {

    var lDiv = document.createElement('div');

    document.body.appendChild(lDiv);

    if (pStyle != null) {
        lDiv.style = pStyle;
    }

    lDiv.style.fontSize = "" + pFontSize + "px";
    lDiv.style.position = "absolute";
    lDiv.style.left = -1000;
    lDiv.style.top = -1000;

    lDiv.innerHTML = pText;
        const width = lDiv.clientWidth;

    document.body.removeChild(lDiv);
    lDiv = null;

    return width;

}

Next, for your splitThroughPixel, you just have to loop through each words, get the pixels, and check if the sentence is greater than the width or not. If it's greater, add the previous string to the result.

function splitThroughPixel(string, width, size, style){

    const words = string.split(' ');
    const response = [];
    let current = '';

    for(let i=0; i<words.length; i++){

        const word = words[i];
        const temp = current + (current == '' ? '' : ' ') + word;

        if(getWidth(temp, size, style) > width){
            response.push(current.trim());
            current = '';
        }else{
            current = temp;
        }

    }

    return response;

}

Example

const myString = "Lorem Ipsum has been the industry's standard dummy text ever since the 1500s";
console.log(splitThroughPixel(myString, 100, 14));

And the response of the example would be an array like this:

["Lorem Ipsum has", "the industry's", "dummy text ever"]

Demo

https://jsfiddle.net/ChinLeung/rqp1291r/2/

Chin Leung
  • 14,621
  • 3
  • 34
  • 58
1

Instead of creating and removing elements each loop as other answers suggested, which would be bad performance-wise, you can reuse the same element and add/reset the innerHTML, based on the current width.

function wrapText(text, maxWidth) {
    const words = text.split(' ');
  
    var el = document.createElement('div');
    document.body.appendChild(el);

    el.style.position = "absolute";
    let rows = [];
    let row = [];
    let usedIndex = 0;
    
    // loop through each word and check if clientWidth is surpassing maxWidth
    for(let i = 0; i < words.length; i++) {
      const word = words[i];
      el.innerHTML += word;
      if (el.clientWidth > maxWidth) {
        rows.push(el.innerHTML);
        usedIndex = i;
        el.innerHTML = "";
      } else {
        el.innerHTML += " ";
      }
    }
    
    // handle remaining words
    words.splice(0, usedIndex);
    rows = rows.concat(words.join(" "));
    
    document.body.removeChild(el);

    return rows;
}

const text = "Lorem Ipsum has been the industry's standard dummy text ever since the 1500s";
console.log(wrapText(text, 100));
p {
  position: relative;
}

p::before {
  outline: 1px solid red;
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  width: 100px;
  content: '';
}
Jorjon
  • 5,316
  • 1
  • 41
  • 58
  • sorry I am asking too late but I need this. what if my string includes some html tags like span or p tag. then it is not calculating perfect for me – Rahul Dudharejiya Jan 23 '20 at 10:53
0

You can create a temporary div that has white-space: nowrap and display: inline and add words to it, then test it's width.

This solution will not allow a substring to go beyond your pixel limit, unless one entire word goes beyond that pixel limit.

let myString = "Ipsum has been the industry's standard dummy text ever since the 1500s";
let split = splitThroughPixel(myString, 100);
console.log(split);

function splitThroughPixel(string, px) {
 let words = string.split(' ');
 let split = [];

 let div = document.createElement('div');
 div.style.cssText = 'white-space:nowrap; display:inline;';
 document.body.appendChild(div);

 for (let i = 0; i < words.length; i++) {
  div.innerText = (div.innerText + ' ' + words[i]).trim();
  let width = Math.ceil(div.getBoundingClientRect().width);
  if (width > px && div.innerText.split(' ').length > 1) {
   let currentWords = div.innerText.split(' ');
   let lastWord = currentWords.pop();
   split.push(currentWords.join(' '));
   div.innerText = lastWord;
  }
 }

 if (div.innerText !== '') {
  split.push(div.innerText);
 }

 document.body.removeChild(div);

 return split;
}
KevBot
  • 17,900
  • 5
  • 50
  • 68