MaiaVictor
MaiaVictor

Reputation: 52977

Calculate font-size required for text to fill desired space

How to calculate the font-size required for a text element to fill desired space? For example:

txt = "this text should fill the div precisely"
el = $("<div/>")
    .html(txt)
    .width(200)
    .css({
        fontFamily: "arial",
        fontSize: font_size_to_fit(txt,"arial",200)
    });

Edit: I am asking for a function that calculates the size based in a straighforward mathematical formula. It can be an approximation. The solution proposed on the question above is not correct because it involves an interactive DOM manipulation which is too heavy to be placed in a bottleneck area.

Upvotes: 2

Views: 5750

Answers (4)

Scott Sauyet
Scott Sauyet

Reputation: 50787

I've never tried this, and perhaps it's crazy, but instead of all the (unfortunately unhelpful) suggestions to approach this by changing sizes over and over until the text fits, perhaps you could try just two very different font sizes (using no-wrap, of course) such as 10 and 100 and then interpolate or extrapolate your expected value assuming that the relationship should be nearly linear? I don't know if doing two layouts like this is too expensive for your bottleneck. If so, I'm not sure what hope you really have, as fonts can be tricky. But this might work.

Update: I've created a Fiddle using this technique. It seems to work reasonably well. The relevant code is:

var font_size_to_fit = (function() {
    var $test = $("<span/>").appendTo("body").css({
        visibility: "hidden", 
        border: 0, 
        padding: 0, 
        whiteSpace: "pre"
    });
    var minFont = 10, maxFont = 100; 
    return function(txt, fontFamily, size) {
        $test.appendTo("body").css({fontFamily: fontFamily}).text(txt);
        $test.css({fontSize: maxFont + "px"});
        var maxWidth = $test.width();
        $test.css({fontSize: minFont + "px"});
        var minWidth = $test.width();
        var width = (size - minWidth) * (maxFont - minFont) / 
            (maxWidth - minWidth) + minFont;
        $test.detach();
        return width + "px";
    };
}());

This would probably also need some error-checking to ensure we have no division by zero, which would occur when minWidth == maxWidth, most likely when the text supplied is empty. That's left as an exercise for the reader.

Another update: This broke at some point. I updated the Fiddle to include this:

        return (width - .1) + "px";

This fixes an error noted in the comments. But I have no idea what has changed to make it break in between.

Upvotes: 3

Jackson Ray Hamilton
Jackson Ray Hamilton

Reputation: 9466

function font_size_to_fit(text, fontFamily, divSize) {
  var tester = $("<div/>")
  .appendTo("body")
  .css({
    position: "absolute",
    left: "-9999px",
    fontFamily: fontFamily })
  .html(text);

  var width;
  var fontSize = 72;
  do {
    tester.css("fontSize", fontSize);
    fontSize -= 1;
    width = tester.width();
  } while (!(width <= divSize && width >= divSize * .98));
  return fontSize;
}

Maybe something like that? (Note: Doesn't address potential browser minimum font-sizes, didn't want to make the loop too complex.)

(Edited once to fix some potential bugs)

Upvotes: 2

Ennui
Ennui

Reputation: 10190

FitText.js is a great jQuery plugin for this.

It will resize fluidly (as the element changes size it will modify the font-size, not just on document ready or load), allows for min and max font sizes, and has some other options to allow you to tweak its behavior.

Upvotes: 2

Working Demo

var x = 1;
var y = 20; //staring font-size
txt = "this text should fill the div precisely";
$('#element').html(txt).width(200).height(30).css('font-family', 'arial');
while (x == 1) {
    var element = document.querySelector('#element');
    if ((element.offsetHeight < element.scrollHeight) || (element.offsetWidth < element.scrollWidth)) {
        element.style.background = "yellow";
        element.style.fontSize = parseInt(y) + 'px';
        y--;
    } else {
        x = 0;
        element.style.background = "blue";
    }
}

Upvotes: 1

Related Questions