Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change Font Size to automatically fit Div's height/width [duplicate]

Tags:

jquery

css

There have been similar questions asked, but the solutions do mesh with what I'm trying to do. Basically, I have an article with a title (<h1>). I don't want to control the length of the title, but I also don't want the title to appear on multiple lines. Is there a way with css or jQuery to resize text based on the width of a <div> tag?

I know what I would do with pseudocode if I could detect the overlap of the text to the edge of the <div>:

var fontize = $("#title").css("font-size");
var i = /*remove unit from integer*/
while( /*text overlaps div*/ ){
    $("#title").css("font-size", --i+"pt");
}

If there's a CSS attribute I can set that would be even nicer, but I can't seem to find one (overflow wouldn't work in this situation).

like image 425
TCCV Avatar asked Aug 03 '10 22:08

TCCV


People also ask

How do I automatically adjust font size in HTML?

Syntax: font-size-adjust: number|none|initial|inherit; Below are the examples that illustrates the use of font-size-adjust property.

How do I change font size and width?

The font size can be set with vw (viewport) unit, which means the viewport width. The viewport is the browser window size. 1vw = 1% of viewport width. If the viewport is 50cm wide, 1vw is 0.5cm.


3 Answers

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
like image 112
Robert Koritnik Avatar answered Sep 19 '22 19:09

Robert Koritnik


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.

like image 38
chunks_n_bits Avatar answered Sep 18 '22 19:09

chunks_n_bits


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);
like image 32
Clovis Six Avatar answered Sep 18 '22 19:09

Clovis Six