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.

6
  • I really did not get your question, can you tell me more simple way Commented Feb 7, 2018 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 ? Commented Feb 7, 2018 at 17:15
  • What are you using this method for? Commented Feb 7, 2018 at 17:21
  • Possible duplicate of Determine Pixel Length of String in Javascript/jQuery? Commented Feb 7, 2018 at 17:22
  • @MikeMcCaughan it is not a dupe. Your post is about determining pixel, mine is about Splitting through pixel ! Commented Feb 7, 2018 at 17:59

3 Answers 3

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/

Sign up to request clarification or add additional context in comments.

Comments

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: '';
}

1 Comment

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
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;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.