user2613399
user2613399

Reputation: 785

Dynamically change the size of the font size based on text length

I need to display user entered text into a fixed size div. What I want is for the font size to be automatically adjusted so that the text fills the box as much as possible.

I'd probably want to start with a maximum font size and while the text is too big to fit the container, shrink the font size until it fits and the font must be displayed as a single line.

Upvotes: 60

Views: 110449

Answers (11)

gre_gor
gre_gor

Reputation: 6802

There is no need to iterate over font sizes. You just need to calculate the current size of the text and the size of the container and apply the scaling.

const text_el = document.querySelector("#text");
const container_el = document.querySelector("#container");

text_el.addEventListener("input", function(e) {
  container_el.children[0].textContent = this.value;
  resize2fit(container_el)
});

function resize2fit(el) {
  if (!el.parentElement) return;
  el.style.setProperty("--font-size", "1em");
  const {width: max_width, height: max_height} = el.getBoundingClientRect();
  const {width, height} = el.children[0].getBoundingClientRect();
  el.style.setProperty("--font-size", Math.min(max_width/width, max_height/height)+"em");
}

window.addEventListener("resize", function(e) {
    resize2fit(container_el);
});
resize2fit(container_el);
#container {
  width: 100%;
  height: 100px;
  font-size: var(--font-size, 1em);
  white-space: nowrap;
  border: 1px solid blue;
}
#container > * {
  display: inline-block;
}
<input id="text" value="lorem ipsum" />
<div id="container">
  <span>lorem ipsum</span>
</div>

Upvotes: 9

John C
John C

Reputation: 170

Although this is an old question and already has several answers (mostly based on changing the font size until the content fits), I recently had to do something similar with one of our projects.

Initially I used an edited version of Joe's answer (https://stackoverflow.com/a/33492161/587526), but then I began to think about it and realised that the same effect can be obtained much more easily by using a simple "transform: scale(x)" operation. An added advantage is that the content is scaled to exactly fit in the container, whereas adjusting font-size may result in suboptimal text sizes.

Calculating the value of the scale factor is ridiculously simple, just divide the parent container width by the child container width. If the child content is larger than the parent, the resulting value will be < 1.0 - and passing this to "transform: scale()" results in a perfectly redimensioned child element.

Here is the function I use. It uses jQuery, but could also be easily updated to use the available DOM functions directly.

The code is mostly self-explanatory, but I will point out that the child's "display" is initially changed to "inline-block" because elements such as <p>,<span> etc. do not otherwise report their widths.

Something else to be aware of, using "transform: scale()" may affect margins. In my case I needed to ensure that the child elements were centered inside the parent container so I made use of display: flex:

.parent-container {
    display: flex;
    flex-direction: column;
    align-items: center;
}

Hope someone finds this useful!

function resizeToFit(parent, child) {
    let $parent = jQuery(parent);
    let $child = jQuery(child);

    // Temporarily redefine child's display so we
    // can obtain the real width
    let $display = $child.css('display');
    $child.css('display', 'inline-block');


    // Remove any existing inline transform
    $child.css('transform', '');

    // Calculate the scale factor
    let factor = $parent.width() / $child.width();
    if (factor < 1.0) {
        let scale = 'scale(' + factor + ')';
        $child.css({
          '-webkit-transform' : scale,
          '-moz-transform'    : scale,
          '-ms-transform'     : scale,
          '-o-transform'      : scale,
          'transform'         : scale
        });
    }

    // Restore the original display value
    $child.css('display', $display);
};

Upvotes: 4

behdad setoodegan
behdad setoodegan

Reputation: 41

You can try this one, it's working 100% for me.

  function resize(){
    const isOverflown = ({ clientWidth, scrollWidth, clientHeight, scrollHeight}) => scrollWidth > clientWidth || scrollHeight > clientHeight

    const resizeText = ({ element, elements, minSize = 10, maxSize = 512, step = 1, unit = 'px' }) => {
      (elements || [element]).forEach(el => {
        let i = minSize
        let overflow = false

            const parent = el.parentNode

        while (!overflow && i < maxSize) {
            el.style.fontSize = `${i}${unit}`
            overflow = isOverflown(parent)
            el.style.lineHeight = ((el.style.fontSize).slice(0, -2))*1.2+"px"
          if (!overflow) i += step
        }

        el.style.fontSize = (`${i - step - 1}${unit}`)
      })
    }

    resizeText({
      elements: document.querySelectorAll('.text'),
      step: 0.5
    })
  }
  
    if (document.readyState === 'complete') {
    resize();
  } else {
    window.onload = resize();
  }
<div id="outer" style="width:200px; height:80px; border:1px solid red;">
    <div class="text">Lorem ipsum</div>
</div>

<div id="outer" style="width:200px; height:80px; border:1px solid red;">
    <div class="text">Lorem ipsum dolor sit amet, consectetur adipiscing 
    elit. Integer mollis dui felis, vel vehicula tortor cursus nec.</div>
</div>

Upvotes: 0

Tharindu Sathischandra
Tharindu Sathischandra

Reputation: 1994

Based on the logic of the top answer here, I created a no-jQuery version,

const input = document.querySelector('input');
const output = document.querySelector('.output');
const outputContainer = document.querySelector('.container');

function resize_to_fit() {
  let fontSize = window.getComputedStyle(output).fontSize;
  output.style.fontSize = (parseFloat(fontSize) - 1) + 'px';
  
  if(output.clientHeight >= outputContainer.clientHeight){
    resize_to_fit();
  }
}

function processInput() { 
  output.innerHTML =this.value;
  output.style.fontSize = '100px'; // Default font size
  resize_to_fit();
}

input.addEventListener('input', processInput);
<input type="text" placeholder="Add text input here" style="margin: 10px; width:50%;">

<div id="outer" class="container" 
style="width:80%; height:100px; border:2px solid red; font-size:20px;">
  
<div class="output" style="word-break: break-all; word-wrap: break-word;">
</div>

</div>

Upvotes: 38

ecairol
ecairol

Reputation: 6573

Using very little Javascript, you can assign a CSS class with the length of the string, eg: text-length-5 or text-length-20

Then use exclusively CSS to target different font-size according to those class names.

It probably won't work for every case, but it did very well for mine, where the max length was about 10-12 chars.

If you have 100+ or 1000+ chars you can also make a function that receives the length and returns a CSS class, then style base on those ranges.


HTML:

<div class="box">ABC</div>
<div class="box">ABCDE</div>

Javascript (jQuery, but the same idea can be applied to React, or vanilla JS)

$(".box").each((i, el)=> {
  const length = $(el).text().length;
  $(el).addClass("text-length-"+length);
})

CSS:

.box {
  font-size: 16px;
}
.box.text-length-4,
.box.text-length-5,
.box.text-length-6 {
  font-size: 12px;
}

Upvotes: 1

putvande
putvande

Reputation: 15213

Say you have this:

<div id="outer" style="width:200px; height:20px; border:1px solid red;">
    <div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer mollis dui felis, vel vehicula tortor cursus nec</div>
</div>

Then you can do something like:

$(document).ready(function () {
    resize_to_fit();
});

function resize_to_fit(){
    var fontsize = $('div#outer div').css('font-size');
    $('div#outer div').css('fontSize', parseFloat(fontsize) - 1);

    if($('div#outer div').height() >= $('div#outer').height()){
        resize_to_fit();
    }
}

Working Sample

$(document).ready(function() {
  resize_to_fit();
});

function resize_to_fit() {
  var fontsize = $('div#outer div').css('font-size');
  $('div#outer div').css('fontSize', parseFloat(fontsize) - 1);

  if ($('div#outer div').height() >= $('div#outer').height()) {
    resize_to_fit();
  }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="outer" style="width:200px; height:20px; border:1px solid red;">
  <div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer mollis dui felis, vel vehicula tortor cursus nec</div>
</div>

Upvotes: 35

Akin Hwan
Akin Hwan

Reputation: 627

If by text length you mean character count, there is this article which points to a short jquery snippet Change Font Size dynamically based on Character count

http://jsfiddle.net/jfbrLjt2/

 $('.text').each(function(){
 var el= $(this);
   var textLength = el.html().length;
    if (textLength > 20) {
        el.css('font-size', '0.8em');
    }
});

Upvotes: 3

Joe
Joe

Reputation: 37

Thanks putvande for letting me know the general method. Here is an improved version.

  1. Get rid of the callback hell.
  2. Fixed some bugs may cause page hang.

Same HTML:

<div id="outer" style="width:200px; height:20px; border:1px solid red;">
    <div>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer mollis dui felis, vel vehicula tortor cursus nec</div>
</div>

Here is the function:

resize_to_fit($('#outer'), $('#outer div'));

function resize_to_fit(outer, inner) {
    while(inner.height() > outer.height()) {
        var fontsize = parseInt(inner.css('font-size')) - 1;
        inner.css('font-size', fontsize);
        // some browsers(chrome) the min font return value is 12px
        if(fontsize <= 1 || parseInt(inner.css('font-size')) >= fontsize+1)
            break;
    }
}

Upvotes: 1

SRachamim
SRachamim

Reputation: 931

Whoever wants a dynamic function that depends on the width of the element (based on putvande answer):

        $(document).ready(function () {
            var arrEl = ['div1', 'div2', 'div3'];
            resize_to_fit(arrEl);
        });

        function resize_to_fit(arr){
            for (var el in arr) {
                var jEl = $('.' + arr[el] + ' span');
                var fontsize = jEl.css('font-size');
                jEl.css('fontSize', parseFloat(fontsize) - 1);
                if (jEl.width() >= $('.' + arr[el]).width()){
                    resize_to_fit([arr[el]]);
                }        
            }
        }

and the HTML:

<div class="div1">
    <span>Lorem Ipsum</span>
</div>
<br/>
<div class="div2">
    <span>Lorem Ipsum 2</span>
</div>
<br />
<div class="div3">
    <span>Lorem Ipsum 3</span>
</div>

whith CSS:

.div1, .div2, .div3 {
    width: 207px;
    white-space: nowrap;
}

Notice you only set font-size to span inside, and not to the outer div.

Upvotes: 0

colestrode
colestrode

Reputation: 10658

Getting the size in pixels of a string of text is a hard thing to do and depends a lot on the browser and the user's settings, so it will probably be hard to come up with a solution that works in all cases.

By "display user entered text" do you mean the user is typing into a textbox? If so, you can attach a keypress listener that checks the length of the value of the text, then adjusts the font-size accordingly. Here's an example, though you will probably need to change the lengths and font-sizes to meet your needs:

Working Demo

$('#text').on('keypress', function(e) {
    var that = $(this),
        textLength = that.val().length
    ;

    if(textLength > 30) {
        that.css('font-size', '5px');
    } else if(textLength > 20) {
        that.css('font-size', '10px');
    } else if(textLength > 10) {
        that.css('font-size', '15px');
    }
});

Upvotes: 2

Anon
Anon

Reputation: 2874

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        #div1{
            width: 200px;
            height: 200px;
            font-size: 32px;
            line-height: 1.2em;
        }
    </style>
    <script type="text/javascript" src="js/jquery-1.9.1.min.js"></script>
    <script>
        $(function(){
            n = 1;
            while(n==1){
                n = 0
                if ($('#div1 .holder').outerHeight()>$('#div1').outerHeight()){
                    var fz = parseInt($('#div1').css('font-size'));
                    $('#div1').css({'font-size' : fz-1});
                    n = 1
                } else {n = 0}
            }
        });
    </script>
</head>
<body>
    <div id="div1">
        <div class="holder">I need to display user entered text into a fixed size div. What i want is for the font size to be automatically adjusted so that the text fills the box as much as possible.
I'd probably want to start with a maximum font size and while the text is too big to fit the container, shrink the font size until it fits and the font must be displayed as a single line. Is it possible to do this using only css and html.</div>
    </div>
</body>
</html>

Upvotes: 1

Related Questions