Dynamically making font size change so it stays within the container element? - calculator project

Hi guys,

I’m nearly done with this calculator project. A great learner project, especially for learning about using strings and numbers together. My logic works, my buttons work, but when I display the final number it often does not fit in my display.

I am trying to make the font get smaller when the number has more digits, so that it fits in the viewing area.

This is fine:

But then this happens:

I found some examples on stack overflow, but they used jquery ( which I am deliberately avoiding in this project ) and text areas. I am just using a < p> in a < div>.

I guess I somehow need to calculate the width of the final number every time it is displayed, and if it is more than some limit, scale down the font.

I played with the idea of making the number into scientific notation if it is too wide. But that involved flipping between strings and nums and manipulating the number by concatinating ‘e+’ or ‘e-’ and quickly it became a mess that was sacrificing mathematical accuracy for visualization.
It would be better I think to keep the number as is and make it fit by scaling it.

Any pointers in the right direction would be greatly appreciated!

1 Like

I would just limit the number of digits displayed to the amount that fits properly on the screen. If you scale them down the really big numbers are going to be to small to read. I think that is how calculators work. They limit the number to the size that can be displayed.

Thanks for the suggestion.

Javascript already seems to limit itself to 21 characters, so this is as small as it would get, which is ok I think.

I just need a way to change the font size (and position in the window) depending on number width to do that. The current built in calculator for mac uses the same concept.

Maybe I need a separate css class which only gets added by the js if num width > x… good strategy, but the how has me stumped

Ok I see. I didn’t see your project but one of the solutions may be to detect overflow. Let’s say this is your display:

<div id="el" style="font-size: 18px;width: 200px; border: 1px solid black; height: 
20px;">0000000000000000000000
</div>

You have to set the font-size in order to retrtieve it. Then detect overflow and if it evaluates to true run a loop that decreases your font size by 1 on each iteration. When your content stops overflowing set the font-size to the current state and that’s it. Code sample below:

function isOverflown(element) {
    return element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth;
}
let el = document.getElementById('el');
let fontSize = parseInt(el.style.fontSize);
for (let i = fontSize; i >= 0; i--) {
    let overflow = isOverflown(el);
    if (overflow) {
     fontSize--;
     el.style.fontSize = fontSize + "px";
    }
}

Don’t know if that’s what you need but that is one of the ways to go :wink:

2 Likes

Ah very cool strategy, this makes sense. Thanks!!

I have a rookie question for you. Why:
let el = instead of var el = ?
It’s an ECMA 6 thing right? Will there be any backwards compatibility issues with older browsers? What is the advantage of using let?

I was trying to do the font in em… but I checked the MDN docs and found that clientHeight measures pixels. So pixels it is, I guess. Out of curiosity, is there an easy way to do it with em?

It was shrinking nicely, but then it would stay small even when it could be bigger and still fit. So I made a reset function as well, and called it before every resize call.

resizeDisplay : function() {
function isOverflowing(el){
return el.scrollHeight > el.clientHeight || el.scrollWidth > el.clientWidth;
}
var eye = document.getElementById(“entry-eye-p”);
var fontSize = parseInt(eye.style.fontSize);
var paddingAbove = parseInt(eye.style.paddingTop);

    for (var i=fontSize; i>=0; i--){
        var overflow = isOverflowing(eye);
        if (overflow) {
            fontSize--;
            paddingAbove += 0.41;
            eye.style.fontSize = fontSize + "px";
            eye.style.paddingTop = paddingAbove + "px";
        }
    }
},
resetDisplay : function() {
    var eye = document.getElementById("entry-eye-p");
    eye.style.fontSize = "27px";
    eye.style.paddingTop = "3px";
},

It wouldn’t stay in the middle of the div when it got smaller, instead moving towards the top. So I added the += 0.4 increment to padding top every time the font size decreased by 1. That has it staying roughly in the middle.
There has got to be a better way to do that though… maybe with percentages?

Last thing to do is add my signature party mode easter egg. It should be live tomorrow.

Thanks again for the help!

1 Like

Actually I no longer use var. Let has the advantage of code block scoping instead of function scoping. Especially useful in loops.

for (let i = fontSize; i >= 0; i--) {
      //'i' is only visible here
}
for (let i = sthElse; i >= 0; i--) {
      // I can use 'i' again in the same function because it is scoped to the nearest code block
}

It also helps with the closure problem in loops because it binds fresh value to i on each loop

Yes it is ECMA 6. Yes there will be issues for example in older IE but you can always use compiler like Babel that will output the “older” javascript for you. I myself don’t care about older browser. I write code for modern browsers or node.js environment. These older browsers will soon die out anyway.

I think that in the end all values like em, % ,vh are translated to pixels by a browser. Not sure though…just my guess :wink:
I think it will work with em just like with pixels you just need to slightly change the loop to iterate decimal values like this:

let fontSize = parseInt(el.style.fontSize).toFixed(2);

for (let i = fontSize; i > 0; i -= 0.1) {
  let overflow = isOverflown(el);
  if (overflow) {
    fontSize -= 0.1;
    el.style.fontSize = fontSize + "em";
  }
}

Good job with that reset function :wink: I din’t notice this problem before

1 Like

Thanks for the info!

The result is here.

I guess all it really needs is some smoothing out of the result number vertical alignment as it gets smaller.