Breaking a string by character count is trivially easy in JavaScript. I needed to break a string based on the visual width though. There are challenges here from each character in a string having a unique width depending on the caracter, font weight, font size, and other settings. I needed to do this for a project. While I’m not proud of the solution, it works, and is something born out of working with what I have. In some other environments, there are functions for information on font metrics that would be assistive. But these are not present in JavaScript (though the Canvas APIs have something that comes close, they don’t help here because they can’t take CSS into account).
The solution that I used took a string abd broke it into words. Each word was added to an element one at a time. I wrapped each word in a <span/> tag. The offsetWidth and offsetHeight on the span elements indicate how much space it is take up. I also had to wrap spaces in <span/> tags. Each time I added a word or space to a parent element, I measured the width to see if I had exceeded some maximum tolerate width. If that width hasn’t been exceeded, I keep going. If it has, I remove the last word that was added and grab all the other words and save them. They are all the words I could fit on that line. The word I removed from the string is then used to start a new string. The process repeats.
A parent element is needed for this process so that the string can inherit display settings during measurement. This could be a zero opacity parent element or something positioned offstring to ensure that it doesn’t get displayed. Though in my testing, this process happens fast such that the string is never displayed while being processed.
I don’t like adding things to the DOM for the sake of just getting a measurement. Some part of me has concerns about side effects of adding and removing items from the DOM, such as exacerbating the effects of some bug that might be present or increasing the frequency of garbage collection cycles. But right now, this is the best solution that I see.
function BreakAtWidth(text,parentElement, maxWidth) {
if(maxWidth == null) {
maxWidth = 80;
}
if(typeof parentElement == 'string') {
parentElement = document.getElementById(parentElement);
}
var tempChild = document.createElement('span');
tempChild.style.opacity = 0.0;
parentElement.append(tempChild);
var textParts = text.split(' ');
var elementParts =[];
var elementPartsCombinedString = '';
var brokenParts = [];
textParts.forEach(element => {
elementParts.push(`<span>${element}</span>`);
elementParts.push(`<span> </span/>`)
});
for(var i=0;i<elementParts.length;++i) {
elementPartsCombinedString += elementParts[i];
tempChild.innerHTML = elementPartsCombinedString;
const width = tempChild.offsetWidth;
if(width >= maxWidth) {
var resultString = elementPartsCombinedString.substring(0, elementPartsCombinedString.length - elementParts[i].length);
if(resultString == '') {
brokenParts.push(elementPartsCombinedString);
elementPartsCombinedString = '';
}
else {
brokenParts.push(resultString);
elementPartsCombinedString = elementParts[i];
}
}
}
if(elementPartsCombinedString != '') {
brokenParts.push(elementPartsCombinedString);
}
var cleanStringList = [];
brokenParts.forEach(part=> {
cleanStringList.push(part.replaceAll('<span>','').replace('span/>',''));
}) ;
tempChild.remove();
return cleanStringList;
}
Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.
Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet









