resize font to fit in a div (on one line)

CSS no, Javascript yes

There's no way you could do this using CSS, but you can do it in javascript/jQuery. To help you with your pseudo code because you already know what to do. It's just that you don't know how to detect excess width.

The best way would be to have a DIV with following (at least) style:

position: absolute;
visibility: hidden;
white-space: nowrap;
font-family: /* same as your title's */

then copy your text to it and set some starting font size. Then you can iterate through your while loop and stop when div's width is appropriate. Then set calculated font size to your original title.

This way this checking will be hidden from user's eyes and it will therefore work faster as well.

BTW: This is the usual way how auto growing textarea scripts work. They use dummy divs with same style settings as the original text area and adjust area's height as the user types in text. So Text area can be quite small at first but if user types in lots of text it will auto grow to accommodate content.

While loop optimization

You could optimize your while loop to decrease the number of iterations considerably by doing this:

  1. Set a starting font size.
  2. get test DIV's width.
  3. calculate width factor between orig_div and test_div.
  4. adjust font size by this factor rather than increase/decrease by one unit
  5. test test_div width

I had a similar issue, which made me write my own plugin for this. Have a look at jquery-quickfit (which is quite similar to Robert Koritnik's solution, which I really like).

In order to prevent the headline to span multiple lines, just add a css style of:

white-space:nowrap;

to the element.

After including jquery and quickfit in the header. You can trigger quickfit with:

$('h1').quickfit();

It meassures and calculates a size invariant meassure for each letter of the text to fit and uses this to calculate the next best font-size which fits the text into the container.

The calculations are cached, which makes it very fast when dealing having to fit multiple text or having to fit a text multiple times, like e.g., on window resize (there is almost no performance penalty on resize).

Demo for a similar situation as yours

Further documentation, examples and source code are on the project page.


Here are 3 functions I use frequently to get the text width, height and adjust the size to the container's width.

  • getTextWidth() will return you the actual width of the text contained in the initiator.
  • getTextHeight(width) will return the actual height of the wrapped text contained in the initiator with a certain specified width.
  • autoTextSize(minSize, maxSize, truncate) will size the text within the container so it fits, considering a minimum and maximum size.
  • autoTruncateText() will only show the characters the user can actually see and end the text with '...'.
(function ($) {
  $.fn.getTextWidth = function() {
    var spanText = $("BODY #spanCalculateTextWidth");

    if (spanText.size() <= 0) {
      spanText = $("<span id='spanCalculateTextWidth' style='filter: alpha(0);'></span>");
      spanText.appendTo("BODY");
    }

    var valu = this.val();
    if (!valu) valu = this.text();

    spanText.text(valu);

    spanText.css({
      "fontSize": this.css('fontSize'),
      "fontWeight": this.css('fontWeight'),
      "fontFamily": this.css('fontFamily'),
      "position": "absolute",
      "top": 0,
      "opacity": 0,
      "left": -2000
    });

    return spanText.outerWidth() + parseInt(this.css('paddingLeft')) + 'px';
  };

  $.fn.getTextHeight = function(width) {
    var spanText = $("BODY #spanCalculateTextHeight");

    if (spanText.size() <= 0) {
      spanText = $("<span id='spanCalculateTextHeight'></span>");
      spanText.appendTo("BODY");
    }

    var valu = this.val();
    if (!valu) valu = this.text();

    spanText.text(valu);

    spanText.css({
      "fontSize": this.css('fontSize'),
      "fontWeight": this.css('fontWeight'),
      "fontFamily": this.css('fontFamily'),
      "top": 0,
      "left": -1 * parseInt(width) + 'px',
      "position": 'absolute',
      "display": "inline-block",
      "width": width
    });

    return spanText.innerHeight() + 'px';
  };

  /**
   * Adjust the font-size of the text so it fits the container.
   *
   * @param minSize     Minimum font size?
   * @param maxSize     Maximum font size?
   * @param truncate    Truncate text after sizing to make sure it fits?
   */
  $.fn.autoTextSize = function(minSize, maxSize, truncate) {
    var _self = this,
        _width = _self.innerWidth(),
        _textWidth = parseInt(_self.getTextWidth()),
        _fontSize = parseInt(_self.css('font-size'));

    while (_width < _textWidth || (maxSize && _fontSize > parseInt(maxSize))) {
      if (minSize && _fontSize <= parseInt(minSize)) break;

      _fontSize--;
      _self.css('font-size', _fontSize + 'px');

      _textWidth = parseInt(_self.getTextWidth());
    }

    if (truncate) _self.autoTruncateText();
  };

  /**
   * Function that truncates the text inside a container according to the
   * width and height of that container. In other words, makes it fit by chopping
   * off characters and adding '...'.
   */
  $.fn.autoTruncateText = function() {
    var _self = this,
        _width = _self.outerWidth(),
        _textHeight = parseInt(_self.getTextHeight(_width)),
        _text = _self.text();

    // As long as the height of the text is higher than that
    // of the container, we'll keep removing a character.
    while (_textHeight > _self.outerHeight()) {
      _text = _text.slice(0,-1);
      _self.text(_text);
      _textHeight = parseInt(_self.getTextHeight(_width));
      _truncated = true;
    }

    // When we actually truncated the text, we'll remove the last
    // 3 characters and replace it with '...'.
    if (!_truncated) return;
    _text = _text.slice(0, -3);

    // Make sure there is no dot or space right in front of '...'.
    var lastChar = _text[_text.length - 1];
    if (lastChar == ' ' || lastChar == '.') _text = _text.slice(0, -1);
    _self.text(_text + '...');
  };
})(jQuery);

Tags:

Css

Jquery