Search code examples
htmlcsselementscaling

How do I autoshrink a dynamic h1 element for different text length?


I have an h1 element that changes its HTML dynamically and I want it to auto shrink itself for longer text but return to its fixed font size for shorter text.

Here is the markup:

   <div class="frame" dir="ltr">
      <div class="frame-view">
        <h1> //dynamic text </h1>
      </div>
    </div>

and the styling:

.frame {
  z-index: 2;
  position: relative;
  top: 100px;
}
.frame-view {
  text-align: center;
}

.frame-view h1 {
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
  font-style: normal;
  font-variant: normal;
  font-weight: 200;
  font-size: 40px;
  color: white;
  padding: 0 20px;
}

Here's what the h1 looks like when a long text is in it, on a mobile screen:

enter image description here

rather than staying within the 20px left, right padding and just auto shrinking the text, it stretches past the HTML.

in landscape it looks fine of course because there is enough room to contain it:

enter image description here

So how do I make this h1 auto shrink for longer text and stay within the boundaries ?

UPDATE

to further understand what I am trying to achieve, think of it like this:

I want the container of the h1 to be static and the text adjust its size when it gets closer to the container edges. Imagine a Label being 50 px, now if you put the word "hello" in that label at 20px font size, it would be 20px font size with no problem.. but if you put the words "Hello how are you" it wouldnt fit within the boundaries at 20px font size so it would have to auto shrink itself to stay within the boundaries of the container.


Solution

  • you should start with a given text width in pixels, then measure the width of the given text using the measuretext function. If width is approaching the width of your container, then shrink the font size and remeasure. You should obviously have a limit on how small the text can go before it becomes illegible. Once text gets too small, you have to truncate and add dots onto the end.

    Here is a sample that shows roughly how to do it. You need javascript, and I have included jquery to make it easier. As you add more text, and the text starts getting near the edge of the box, the fontsize will shrink, down to a minimum of 10 pixels. After that text that is too long will be truncated.

    You could also make the text grow again if it got shorter using similar functions but in reverse.. growing the font size till it just overflowed, then back one step. However I have not included code for that.

    <!DOCTYPE html>
    <html>
    
    <head>
      <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
    </head>
    
    <body>
      <br>Enter your text:
      <p></p>
      <input type=text id=input_id style='width: 300px'></input>
      <p></p>
      <canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;">
        Your browser does not support the HTML5 canvas tag.</canvas>
      <script>
        var txt = "Hello World more"
        var c = document.getElementById("myCanvas");
        var ctx = c.getContext("2d");
    
        var fontsize = 30
         ctx.font = fontsize.toString() + "px Arial";
         // ctx.fillText("width:" + ctx.measureText(txt).width, 10, 50);
         // ctx.fillText(txt, 10, 100);
    
        $('#input_id').val(txt)
         ObserveInputValue('')
         ObserveInputValue(txt)
    
         // a bit crude but simple and it works: watch the input object every tenth sec
         setInterval(function() {
          ObserveInputValue($('#input_id').val());
        }, 100);
    
        function ObserveInputValue(iv) {
          if (iv != txt) {
            console.log('input value changed to: ' + iv)
            txt = iv
            ctx.clearRect(0, 0, 300, 150)
    
            textwid = ctx.measureText(txt).width.toFixed(0)
    
            // adjust font size so that text just fits
            var maxfont = 50
            var minfont = 10
            while (textwid > 290 && fontsize > minfont) {
              sizedecrease = Math.max(1, fontsize * 0.1).toFixed(0) // decrease by 10% or 1 pixel whichever is greater
              fontsize -= sizedecrease
              ctx.font = fontsize.toString() + "px Arial";
    
              textwid = ctx.measureText(txt).width.toFixed(0)
            }
            // if text at min and still too long truncate text
            while (textwid > 290 && fontsize <= minfont) {
              // chop off last characters of text
              txt = txt.substring(0, txt.length - 1)
              textwid = ctx.measureText(txt).width.toFixed(0)
    
              $('#input_id').val(txt)
            }
    
            // exercise to the reader.. increase font size again if user makes text shorter
    
            ctx.fillText("width:" + textwid, 10, 50);
            ctx.fillText("font:" + fontsize, 10, 100);
            ctx.fillText(txt, 10, 150);
    
          }
        }
      </script>
    
    
    </body>
    
    </html>