Measuring text height

Written by Richard Heyes, on 14th January 2013

Measuring the width of text is a simple affair of using the context.measureText method. This method however only provides the width of the text - not the height. Normally this isn't so bad - except when you absolutely need the height - it's not available. Fortunately, this can be resolved by using the below function. This function takes the text, whether it's bold or not, the font and the point size (eg 12pt = 12) and adds it to a dynamically generated div that's positioned off-screen. This div can then be measured using the offsetWidth and the offsetHeight and these measurements can then be used as the dimensions.

A point worthy of note is that adding elements to the DOM has a cost - and in some browsers, this can slow down an animation. So for this reason the function uses a cache so that it doesn't repeatedly measure the same thing over and over again. This method of measuring the text height is used in RGraph to good effect when displaying multi-line text.

//
// Measures text by creating a cdiv in the document and adding the relevant text to it.
// Then checking the .offsetWidth and .offsetHeight. Because adding elements to the DOM is not particularly
// efficient in animations (particularly) it caches the measured text width/height.
// 
// @param  string text   The text to measure
// @param  bool   bold   Whether the text is bold or not
// @param  string font   The font to use
// @param  size   number The size of the text (in pts)
// @return array         A two-element array of the width and height of the text
//
function measureText(text, bold, font, size)
{
    // This global variable is used to cache repeated calls with the same arguments
    var str = text + ':' + bold + ':' + font + ':' + size;
    if (typeof(__measuretext_cache__) == 'object' && __measuretext_cache__[str]) {
        return __measuretext_cache__[str];
    }

    var div = document.createElement('DIV');
        div.innerHTML = text;
        div.style.position = 'absolute';
        div.style.top = '-100px';
        div.style.left = '-100px';
        div.style.fontFamily = font;
        div.style.fontWeight = bold ? 'bold' : 'normal';
        div.style.fontSize = size + 'pt';
    document.body.appendChild(div);
    
    var size = [div.offsetWidth, div.offsetHeight];

    document.body.removeChild(div);
    
    // Add the sizes to the cache as adding DOM elements is costly and can cause slow downs
    if (typeof(__measuretext_cache__) != 'object') {
        __measuretext_cache__ = [];
    }
    __measuretext_cache__[str] = size;
    
    return size;
}