skip to Main Content

Because there is no auto font size feature in CSS, I’m trying to create some code that will compensate this function, it works like this:
When the inner elements of the container are overflow the container height, the font size reduces by -1px each iteration until its stops being overflow.

document.addEventListener(
  'DOMContentLoaded',
  function() {
    jQuery
      (
        function($) {
          function GetResponsiveValue() {
            return parseFloat($('body').css('--responsive-value'));
          }

          function SetResponsiveZoomValue(value) {
            $('body').css('--responsive-value', value + 'px'); // I dont know why this does not working here, its should work...
          }

          $('.responsive-container').each(
            function(i, value) {
              var timeout = 0;
              while (value.offsetHeight < value.scrollHeight && timeout <= 100) {
                var currentResponsiveValue = GetResponsiveValue();
                SetResponsiveZoomValue(currentResponsiveValue - 1);
                timeout++;
              } //Actually the font size changes after the iteration is complete, but its should be changed during the each iter.
            }
          );
        }
      );
  }
);
body {
  --responsive-value: 10px;
}

.responsive-container {
  height: 100px;
  width: 300px;
  background-color: yellow;
}

.txt-1 {
  display: block;
  font-size: calc(20px + var(--responsive-value));
}

.txt-2 {
  display: block;
  font-size: calc(30px + var(--responsive-value));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.4.0/jquery.min.js"></script>

<body>
  <div class="responsive-container">
    <span class="txt-1">
      Text size calc(20px + var(--responsive-value))
    </span>
    <span class="txt-2">
      Text size calc(30px + var(--responsive-value))
    </span>
  </div>
</body>

But, the problem is actually the font size changes after the iteration is complete, but its should be changed during the each iter.
How possible to make it work like "reduce font size -1px, check if now overflow, do it again, else stop"?

2

Answers


  1. I adapted a different solution I found elsewhere to this problem, it has served me well so maybe it will help you too.

    You can play around with the buffer (maybe even negative values will work better for you) and ratio.

    const fitFontSize = (
      text,
      maxFontSize,
      maxWidth,
    ) => {
      // approximate these by taking some representative samples
      const letterToWidthRatio = 0.82;
      const buffer = 4;
    
      let maxWordLength: number = text.length;
    
      const words = text.split(' ');
      if (words.length > 1) {
        const largestWord = words.reduce((a, b) => (a.length > b.length ? a : b));
        maxWordLength = largestWord.length;
      }
    
      const newFontSize = Math.min(
        maxFontSize,
        Math.ceil(maxWidth / (maxWordLength * letterToWidthRatio) + buffer)
      );
    
      if (!isNaN(newFontSize)) {
        return newFontSize;
      } else {
        return maxFontSize;
      }
    };
    
    Login or Signup to reply.
  2. Consider using CSS like font-size: 1.25vw; which gives you your font adjustment based on the initial value. I just randomly chose an initial value for that font size.

    Here I do that and ONLY have the JavaScript to show the sizes 250ms after the resize – to keep the event from triggering so much, I "debounce" it which I borrowed from https://stackoverflow.com/a/75988895/125981

    const debounce = (callback, wait) => {
      let timeoutId = null;
      return (...args) => {
        window.clearTimeout(timeoutId);
        timeoutId = window.setTimeout(() => {
          callback(...args);
        }, wait);
      };
    }
    $('.responsive-container').on('resizeDebounced', function(event) {
      let currentSize = {
        txt1: $(this).find('.txt-1').css('font-size'),
        txt2: $(this).find('.txt-2').css('font-size'),
        container: $(event.target).css('font-size')
      };
      $(".show-place").text(JSON.stringify(currentSize));
    });
    
    const handleResize = debounce((mouseEvent) => {
      // Do stuff with the event!
      $('.responsive-container').trigger('resizeDebounced')
    }, 250);
    $(window).on('resize', debounce(handleResize, 250)).trigger('resize');
    body {
      font-size: 16px;
      --responsive-value: 0.625em;
      display: grid;
      place-items: center;
      row-gap: 5em;
    }
    
    .responsive-container {
      display: grid;
      place-items: center;
      height: 6.25em;
      width: 18.75em;
      background-color: yellow;
      font-size: 1.25vw;
    }
    
    .txt-1 {
      font-size: calc(1.25em + var(--responsive-value));
    }
    
    .txt-2 {
      font-size: calc(1.875em + var(--responsive-value));
    }
    
    .show-place {
      margin-top: 1em;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
    
    <body>
      <div class="responsive-container">
        <div class="txt-1">
          Text size calc(1.25em + var(--responsive-value))
        </div>
        <div class="txt-2">
          Text size calc(1.875em + var(--responsive-value))
        </div>
      </div>
      <div class="show-place"></div>
    </body>

    Similar snippet but no JavaScript to show a minimal implementation. Borders just to show what is where. Flex added to keep things in the container in a column.

    body {
      font-size: 16px;
    }
    
    .responsive-container {
      display: flex;
      flex-flow: column;
      width: 18.75em;
      background-color: yellow;
      font-size: 3vw;
      text-decoration: line-through;
    }
    
    .txt-1 {
      font-size: 2em;
      border: solid 1px #FFAF00;
    }
    
    .txt-2 {
      font-size: 3em;
      border: solid 1px #00FF00;
    }
    <body>
      <div class="responsive-container">
        <div class="txt-1">
          Text size calc(1.25em + var(--responsive-value))
        </div>
        <div class="txt-2">
          Text size calc(1.875em + var(--responsive-value))
        </div>
      </div>
    </body>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search