How to change the font-size in JS to fit its container?

Viewed 176

I am making a Simple Calculator App using vanilla JS and I have a div element to display the numbers. But when the textContent within it exceeds its width it starts looking ugly. I want to make the font-size smaller so that its always within the width.

This is how I am adding numbers to my display div

for(let i=0; i<numbers.length; i++){
    numbers[i].addEventListener("click", function(){
        display.textContent += this.textContent;
    })
}

When the textContent is less it looks good

enter image description here

But when the textContent exceeds the width it looks bad

enter image description here

The original code here.

//Selectors
let numbers = document.querySelectorAll(".numbers")
let operators = document.querySelectorAll(".operators")

let equalto = document.querySelector(".equalto")
let clear = document.querySelector(".clear")
let backspace = document.querySelector(".backspace")
let plusMinus = document.querySelector(".plus-minus")
let dot = document.querySelector(".dot")

let display = document.querySelector(".display")
let output = document.querySelector(".output")

let equaltoPressed = false;

//Event Listeners
for(let i=0; i<numbers.length; i++){
 numbers[i].addEventListener("click", function(){
  if (equaltoPressed){
   display.textContent = "";
   equaltoPressed = false;
  }
  //if condition so that if the display has "Infinity" on it, we don't append digits
  if ("0123456789.+-×÷".includes(display.textContent[display.textContent.length-1]) || display.textContent == "")
   display.textContent += this.textContent;
  evaluate();
 })
}

for(let i=0; i<operators.length; i++){
 operators[i].addEventListener("click", function(){
  equaltoPressed = false;
  if ("+-×÷".includes(display.textContent[display.textContent.length-1]))
   display.textContent = display.textContent.substring(0,display.textContent.length-1) + this.textContent;
  else
   display.textContent += this.textContent;
 })
}

equalto.addEventListener("click", function(){ 
 if (output.textContent !== ""){
  display.textContent = output.textContent;
  output.textContent = "";
  equaltoPressed = true;
 } 
});

clear.addEventListener("click", function(){
 equaltoPressed = false;
 display.textContent = "";
 output.textContent = "";
})


backspace.addEventListener("click", function(){
 equaltoPressed = false;
 display.textContent = display.textContent.substr(0,display.textContent.length-1);
 evaluate();
})


plusMinus.addEventListener("click", function(){
 equaltoPressed = false;
 let expression = display.textContent;
 let flag = true;

 for (let i=expression.length-1; i>=0; i--){
  if ("+-×÷".includes(expression[i])){
   if (expression[i] !== "-")
    expression = expression.substring(0,i+1) + "-" + expression.substring(i+1,expression.length);
   flag = false;
   break;
  }
 }

 if (flag)
  expression = "-"+expression;
 display.textContent = expression;

 evaluate();
})


dot.addEventListener("click", function(){
 if (equaltoPressed)
  display.textContent = "";
 let start = 0;
 for (let i=display.textContent.length-1; i>=0; i--){
  if("+-×÷".includes(display.textContent[i])){
   start = i+1;
   break;
  }
 }
 if (!display.textContent.substring(start,display.textContent.length).includes("."))
  display.textContent += ".";
})

//Functions
function evaluate(){
 let expression = display.textContent;

 for (let i=0; i<expression.length; i++){
  if (expression[i] === "×")
   expression = expression.substring(0,i) + "*" + expression.substring(i+1,expression.length);
  if (expression[i] === "÷")
   expression = expression.substring(0,i) + "/" + expression.substring(i+1,expression.length);
 }

 if("0123456789.".includes(expression[expression.length-1]) && eval(expression) != expression)
  output.textContent = eval(expression);
 else
  output.textContent = "";
}
*{
 border:0;
 margin:0;

}

body{
 min-height: 100vh;
 background: black;
 display: flex;
 flex-direction: column;
 align-items: center;
 justify-content: center;
 font-family: arial;
 font-size: 1.5rem;
 color: #f8f8f8;
}

.row{
 display: flex;
}

.light-grey{
 display: flex;
 align-items: center;
 justify-content: center;
 background-color: #a6a6a6;
 height: 4.2rem;
 width: 4.2rem;
 border-radius:  50%;
 margin: .4rem;
 cursor: pointer;
 color: black;
}

.dark-grey{
 display: flex;
 align-items: center;
 justify-content: center;
 background-color: #333333;
 height: 4.2rem;
 width: 4.2rem;
 border-radius:  50%;
 margin: .4rem;
 cursor: pointer;
}

.yellow{
 display: flex;
 align-items: center;
 justify-content: center;
 background-color: #ff9501;
 height: 4.2rem;
 width: 4.2rem;
 border-radius:  50%;
 margin: .4rem;
 cursor: pointer;
}

#zero{
 width: 9.2rem;
 border-radius:0 50px 50px 0;
 border-top-right-radius: 50px;
 border-bottom-left-radius: 50px;
 border-bottom-right-radius: 50px;
 border-top-left-radius: 50px;
}

.display{
 width: 19.2rem;
 height: 3rem;
 margin: 0 .4rem 2rem .4rem;
 text-align: right;
 font-size: 3rem;
}

.output{
 width: 19.2rem;
 height: 2rem;
 margin: 0 .4rem 2rem .4rem;
 text-align: right;
 font-size: 2rem;
}

i{
 font-size: 1.3rem;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.12.0-2/css/all.min.css" integrity="sha256-46r060N2LrChLLb5zowXQ72/iKKNiw/lAmygmHExk/o=" crossorigin="anonymous" />
<div class="display"></div>
<div class="output"></div>
<div class="row">
  <div class="light-grey clear">AC</div>
  <div class="light-grey plus-minus">+/-</div>
  <div class="light-grey operators">÷</div>
  <div class="yellow backspace"><i class="fas fa-backspace"></i></div>
</div>
<div class="row">
  <div class="dark-grey numbers">7</div>
  <div class="dark-grey numbers">8</div>
  <div class="dark-grey numbers">9</div>
  <div class="yellow operators">×</div>
</div>
<div class="row">
  <div class="dark-grey numbers">4</div>
  <div class="dark-grey numbers">5</div>
  <div class="dark-grey numbers">6</div>
  <div class="yellow operators">-</div>
</div>
<div class="row">
  <div class="dark-grey numbers">1</div>
  <div class="dark-grey numbers">2</div>
  <div class="dark-grey numbers">3</div>
  <div class="yellow operators">+</div>
</div>
<div class="row">
  <div class="dark-grey numbers" id="zero">0</div>
  <div class="dark-grey dot">.</div>
  <div class="yellow equalto">=</div>
</div>

1 Answers

Here, better view it on a Full page. Added a cover elements .display-font and .display-cover around .display, fixed their styles a little.

Then added a construction with observer on the end of your JS, which is looking for changing in .display, and then changes the font-size: n% of the .display-cover depends on the size of a .display-font. Here we need .display-font with a fixed value of font-size. Although we can't change font-size of .display, because we are observing the changes in it and font-size changes causes a circle error.

But everything works fine with 2 cover blocks. Look please.

UPDATED

Add everything above to .output block, missed it on the first try.

UPDATE 2

Added white-space: nowrap; to .display-cover and .output-cover to prevent jumping the text to the next line.

Add a lot of comments and explanations to the part of the code, where the changes in .display and .output are observing.

More about MutationObserver. It is not a quantum mechanics or a high level programming. Used it yesterday for the first time, to be honest.

More about while statement. Used it to check if the .display and .output width.

About rounding bug in your comment (when you are using a ling digits, more than 17 symbols).

  1. This bug can be repeated in your original code.

  2. This bug can be repeated in every online calculator i ever found. If you set 100000000000000000+68 - you will get 100000000000000060. This is some Javascript rounding feature. You can test it here https://www.jqueryscript.net/demo/Calculator-App-jQuery-Bootstrap/.

//Selectors
let numbers = document.querySelectorAll(".numbers")
let operators = document.querySelectorAll(".operators")

let equalto = document.querySelector(".equalto")
let clear = document.querySelector(".clear")
let backspace = document.querySelector(".backspace")
let plusMinus = document.querySelector(".plus-minus")
let dot = document.querySelector(".dot")

let display = document.querySelector(".display")
let output = document.querySelector(".output")

let equaltoPressed = false;

//Event Listeners
for (let i = 0; i < numbers.length; i++) {
  numbers[i].addEventListener("click", function() {
    if (equaltoPressed) {
      display.textContent = "";
      equaltoPressed = false;
    }
    //if condition so that if the display has "Infinity" on it, we don't append digits
    if ("0123456789.+-×÷".includes(display.textContent[display.textContent.length - 1]) || display.textContent == "")
      display.textContent += this.textContent;
    evaluate();
  })
}

for (let i = 0; i < operators.length; i++) {
  operators[i].addEventListener("click", function() {
    equaltoPressed = false;
    if ("+-×÷".includes(display.textContent[display.textContent.length - 1]))
      display.textContent = display.textContent.substring(0, display.textContent.length - 1) + this.textContent;
    else
      display.textContent += this.textContent;
  })
}

equalto.addEventListener("click", function() {
  if (output.textContent !== "") {
    display.textContent = output.textContent;
    output.textContent = "";
    equaltoPressed = true;
  }
});

clear.addEventListener("click", function() {
  equaltoPressed = false;
  display.textContent = "";
  output.textContent = "";
})


backspace.addEventListener("click", function() {
  equaltoPressed = false;
  display.textContent = display.textContent.substr(0, display.textContent.length - 1);
  evaluate();
})


plusMinus.addEventListener("click", function() {
  equaltoPressed = false;
  let expression = display.textContent;
  let flag = true;

  for (let i = expression.length - 1; i >= 0; i--) {
    if ("+-×÷".includes(expression[i])) {
      if (expression[i] !== "-")
        expression = expression.substring(0, i + 1) + "-" + expression.substring(i + 1, expression.length);
      flag = false;
      break;
    }
  }

  if (flag)
    expression = "-" + expression;
  display.textContent = expression;

  evaluate();
})


dot.addEventListener("click", function() {
  if (equaltoPressed)
    display.textContent = "";
  let start = 0;
  for (let i = display.textContent.length - 1; i >= 0; i--) {
    if ("+-×÷".includes(display.textContent[i])) {
      start = i + 1;
      break;
    }
  }
  if (!display.textContent.substring(start, display.textContent.length).includes("."))
    display.textContent += ".";
})

//Functions
function evaluate() {
  let expression = display.textContent;

  for (let i = 0; i < expression.length; i++) {
    if (expression[i] === "×")
      expression = expression.substring(0, i) + "*" + expression.substring(i + 1, expression.length);
    if (expression[i] === "÷")
      expression = expression.substring(0, i) + "/" + expression.substring(i + 1, expression.length);
  }

  if ("0123456789.".includes(expression[expression.length - 1]) && eval(expression) != expression)
    output.textContent = eval(expression);
  else
    output.textContent = "";
}


// .display and .output font resizing changing addon
let displayCover = document.querySelector(".display-cover");
let outputCover = document.querySelector(".output-cover");
let displaySize;
let outputSize;
// This part is a config of MutationObserver, options for the observer (which mutations to observe) 
let config = {
  attributes: true,
  childList: true,
  characterData: true
};

// Defining new class of the object MutationObserver, which provides the ability to watch for changes being made to the DOM tree. Basically - this class will be executed on every observing changes in '.display'.
let displayObserver = new MutationObserver(function(mutations) {

  displaySize = 100; // we will use it like a base 100% font-size later
  // set font size to '.display-cover'
  displayCover.style.cssText = 'font-size:' + displaySize + '%';
  
  // 'while' statement creates a loop that executes a specified statement as long as the test condition evaluates to true. The condition is evaluated before executing the statement. Each time we set font size -1 - the width of '.display' changes, and we keeping this loop until the width of '.display' will be not more then it's parent '.display-cover' 
  while(display.offsetWidth > displayCover.offsetWidth) {
    displaySize = displaySize - 1; //decrements font size by 1% each loop
    displayCover.style.cssText = 'font-size:' + displaySize + '%';
    // basically in DOM you see only result of the very last loop of this 'while' statment    
  } 
  
});

// Start observing the target node (your '.display') for configured mutations (using config)
displayObserver.observe(display, config);

//----------

// everything the same as above, but for '.output'
let outputObserver = new MutationObserver(function(mutations) {
  outputSize = 100; 
  outputCover.style.cssText = 'font-size:' + outputSize + '%';
  while(output.offsetWidth > outputCover.offsetWidth) {
    outputSize = outputSize - 1;
    outputCover.style.cssText = 'font-size:' + outputSize + '%';
  } 
});

// as you can see, 'config' is the same
outputObserver.observe(output, config);
* {
  border: 0;
  margin: 0;
}

body {
  min-height: 100vh;
  background: black;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-family: arial;
  font-size: 1.5rem;
  color: #f8f8f8;
}

.row {
  display: flex;
}

.light-grey {
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: #a6a6a6;
  height: 4.2rem;
  width: 4.2rem;
  border-radius: 50%;
  margin: .4rem;
  cursor: pointer;
  color: black;
}

.dark-grey {
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: #333333;
  height: 4.2rem;
  width: 4.2rem;
  border-radius: 50%;
  margin: .4rem;
  cursor: pointer;
}

.yellow {
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: #ff9501;
  height: 4.2rem;
  width: 4.2rem;
  border-radius: 50%;
  margin: .4rem;
  cursor: pointer;
}

#zero {
  width: 9.2rem;
  border-radius: 0 50px 50px 0;
  border-top-right-radius: 50px;
  border-bottom-left-radius: 50px;
  border-bottom-right-radius: 50px;
  border-top-left-radius: 50px;
}
.display-font {
  font-size: 3rem;
}
.display-cover {
  width: 19.2rem;
  height: 3rem;
  margin: 0 .4rem 2rem .4rem;
  text-align: right;  
  font-size: 100%;  
  white-space: nowrap;
}

.display {
  font-size: 100%;
  display: inline-block;
}

.output-font {
  font-size: 2rem;
}
.output-cover {
  width: 19.2rem;
  height: 2rem;
  margin: 0 .4rem 2rem .4rem;
  text-align: right;  
  font-size: 100%;
  white-space: nowrap;
}

.output {
  font-size: 100%;
  display: inline-block;
}

i {
  font-size: 1.3rem;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.12.0-2/css/all.min.css" integrity="sha256-46r060N2LrChLLb5zowXQ72/iKKNiw/lAmygmHExk/o=" crossorigin="anonymous" />
<div class="display-font">
  <div class="display-cover">
    <div class="display"></div>
  </div>
</div>
<div class="output-font">
  <div class="output-cover">
    <div class="output"></div>
  </div>
</div>
<div class="row">
  <div class="light-grey clear">AC</div>
  <div class="light-grey plus-minus">+/-</div>
  <div class="light-grey operators">÷</div>
  <div class="yellow backspace"><i class="fas fa-backspace"></i></div>
</div>
<div class="row">
  <div class="dark-grey numbers">7</div>
  <div class="dark-grey numbers">8</div>
  <div class="dark-grey numbers">9</div>
  <div class="yellow operators">×</div>
</div>
<div class="row">
  <div class="dark-grey numbers">4</div>
  <div class="dark-grey numbers">5</div>
  <div class="dark-grey numbers">6</div>
  <div class="yellow operators">-</div>
</div>
<div class="row">
  <div class="dark-grey numbers">1</div>
  <div class="dark-grey numbers">2</div>
  <div class="dark-grey numbers">3</div>
  <div class="yellow operators">+</div>
</div>
<div class="row">
  <div class="dark-grey numbers" id="zero">0</div>
  <div class="dark-grey dot">.</div>
  <div class="yellow equalto">=</div>
</div>

Related