JohWies
JohWies

Reputation: 107

Getting character width in Javascript

I would like to get the width of a character for calculating the position of any given character (in pixels) inside a string. I figure this would be easy since I am using a monospace font but I have a problem.

I have tried the suggestions in this answer but this doesn't work for large strings. The precision is too poor which means that it works and I can get the position of the first few characters in the string but after, say 10 characters, the precision is so bad that the position I get for the character is so far off it's actually giving the position of the character before.

What I would like to do is get the width of a character so I can just write something like this:

var charWidth = ???;
var position = 5; // Shuold get the position of the fifth character in the string
var calculatedPosition = charWidth * position;

Upvotes: 4

Views: 7270

Answers (2)

see
see

Reputation: 619

Here's a native javascript solution:

What we do is we create an element with a width of 1ch. ch is a css unit that denotes the width of the 0 character of the font. For monospaced fonts, this would be the width of all characters.

// An optional parent element that uses the required font family and size can be specified.
const singleCharacterWidth = (parent = document.body) => {

  const span = document.createElement("span");
  span.style.width = "1ch";
  span.style.position = "fixed";
  
  // The font family and font size can also directly be specified
  // span.style.fontFamily = "Source Code Pro";
  // span.style.fontSize = "24px";

  parent.appendChild(span);
  const width = span.getBoundingClientRect().width;
  parent.removeChild(span);

  return width;
};

const singleCharacterWidth = (parent = document.body) => {
  const span = document.createElement("span");
  span.style.width = "1ch";
  span.style.position = "fixed";

  parent.appendChild(span);
  const width = span.getBoundingClientRect().width;
  parent.removeChild(span);

  return width;
};

const snippetElem = document.querySelector(".snippet");

const charWidth = singleCharacterWidth(snippetElem);

console.log("Single character width:", charWidth)

// Multiplying the width of a single character by the length of the text
// should be equal (or very close) to the computed length of the element
console.log("width * length :", charWidth * snippetElem.innerText.length);
console.log("computed length:", snippetElem.getBoundingClientRect().width);
.snippet {
  font-family: monospace;
}
<span class="snippet">This is a snippet of text</span>

Upvotes: 0

Rachel Gallen
Rachel Gallen

Reputation: 28563

Try this solution, developed by Ben Ripkens

CSS:

.textDimensionCalculation {
    position: absolute;
    visibility: hidden;
    height: auto;
    width: auto;
    white-space: nowrap;
}

JS:

var calculateWordDimensions = function(text, classes, escape) {
    classes = classes || [];

    if (escape === undefined) {
        escape = true;
    }

    classes.push('textDimensionCalculation');

    var div = document.createElement('div');
    div.setAttribute('class', classes.join(' '));

    if (escape) {
        $(div).text(text);
    } else {
        div.innerHTML = text;
    }

    document.body.appendChild(div);

    var dimensions = {
        width : jQuery(div).outerWidth(),
        height : jQuery(div).outerHeight()
    };

    div.parentNode.removeChild(div);

    return dimensions;
};

On his blog he writes

With the help of this little snippet we can now calculate the text dimensions like this.:

var dimensions = calculateWordDimensions('42 is the answer!'); <!--obviously a hitchhikers guide fan, lol --->

console.log(dimensions.width);
console.log(dimensions.height);

An alternate [jquery] solution has been written also by Phil Freo

$.fn.textWidth = function(text, font) {
    if (!$.fn.textWidth.fakeEl) $.fn.textWidth.fakeEl = $('<span>').hide().appendTo(document.body);
    $.fn.textWidth.fakeEl.text(text || this.val() || this.text()).css('font', font || this.css('font'));
    return $.fn.textWidth.fakeEl.width();
};

Upvotes: 4

Related Questions