Math calculations

How to code a calculator web app

By powersjo | Powersjo Technical Blog | 24 Apr 2022


Creating your own calculator web app is a useful project to help beginners learn code and website hosting. When I started this project, I figured the hardest part to code would be the Javascript. This is because I thought since the logic lived there, it would be the most complicated. While this may be true for a simple calculator app, I decided I wanted something more.

I wanted a web app that was responsive and maintained its look no matter what the screen size. I also wanted it to prefer mobile devices. That way, you can load it from any browser, you can save the site to your phone, and the web app acts and looks like a normal calculator app you downloaded from an app store.

With this goal in mind, the most complicated part came from the CSS code. I find that you learn the most when you document your project by teaching or writing about it. Lets start off with the HTML page:

HTML

For the HTML piece, I wanted to ensure that someone can add this web app to their phone and it will function like an app. For that to function, I found https://realfavicongenerator.net/ that resizes your icon and gives you code to enable that on multiple devices. To find a free icon, I used https://icons8.com/.

The JavaScript and CSS are split off into separate files which are referenced in the code.

<html>
<head>
    <!--This meta below disables the pinch in mobile for zoom in and out-->
    <meta name="viewport" content="width=device-width, user-scalable=no">
    <link rel="stylesheet" href="calculator.css">

    <!-- Big thanks to https://realfavicongenerator.net/ for this easy icon work.
         Thanks to https://icons8.com/ for the free icon. -->
    <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
    <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
    <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
    <link rel="manifest" href="/site.webmanifest">
    <link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">
    <meta name="msapplication-TileColor" content="#9f00a7">
    <meta name="theme-color" content="#ffffff">

    <title>Calculator - Powersjo</title>
    <script src="calculator.js"></script>
    <audio id="audio" src="sound/click.wav" autoplay="false"></audio>
</head>
<body>
  <div id="outer">
    <div id="inner_fixed">
      <div class="display">
        <p class="calculation" id="calculation" >0</p>
        <p class="cursor">|</p>
      </div>
    </div>
    <div id="inner_remaining">
      <div class="button_container">
          <div class="cal_button">
            <button class="button" id="AC" onclick="forClear()">AC</button>
          </div>
          <div class="cal_button">
            <button class="button" onclick="parentheses()">( )</button>
          </div>
          <div class="cal_button">
            <button class="button" onclick="percentage()">%</button>
          </div>
          <div class="cal_button">
            <button class="button" onclick="forDisplay('/')">/</button>
          </div>
          <div class="cal_button">
            <button class="button" onclick="forDisplay('7')">7</button>
          </div>
          <div class="cal_button">
            <button class="button" onclick="forDisplay('8')">8</button>
          </div>
          <div class="cal_button">
            <button class="button"onclick="forDisplay('9')">9</button>
          </div>
          <div class="cal_button">
            <button class="button" onclick="forDisplay('*')">x</button>
          </div>
          <div class="cal_button">
            <button class="button" onclick="forDisplay('4')">4</button>
          </div>
          <div class="cal_button">
            <button class="button" onclick="forDisplay('5')">5</button>
          </div>
          <div class="cal_button">
            <button class="button" onclick="forDisplay('6')">6</button>
          </div>
          <div class="cal_button">
            <button class="button" onclick="forDisplay('-')">-</button>
          </div>
          <div class="cal_button">
            <button class="button" onclick="forDisplay('1')">1</button>
          </div>
          <div class="cal_button">
            <button class="button" onclick="forDisplay('2')">2</button>
          </div>
          <div class="cal_button">
            <button class="button" onclick="forDisplay('3')">3</button>
          </div>
          <div class="cal_button">
            <button class="button" onclick="forDisplay('+')">+</button>
          </div>
          <div class="cal_button">
            <button class="button" onclick="forDisplay('0')">0</button>
          </div>
          <div class="cal_button">
            <button class="button" onclick="forDisplay('.')">.</button>
          </div>
          <div class="cal_button">
            <button class="button" onclick="deleteChar()">del</button>
          </div>
          <div class="cal_button">
            <button class="button" onclick="solveDisplay()">=</button>
          </div>
      </div>
    </div>
    <div id="footer_fixed">
      <div id="credit">
        <p>Calculator created by <a href="https://Powersjo.com">Powersjo.com</a></p>
      </div>
    </div>
  </div>
</body>
</html>

Javascript

In order for the buttons to function, I used JavaScript to enable math. As I made the functions, I comment the purpose of the function so I don’t have to guess later. There are some functions that are not necessary such as the error function or the one that adds commas to the display. The core functions are the ones that each button needs to execute from the HTML.


window.onerror = function(message) {
      alert(`${message}`);
}

//This function plays the click sound.
function playSound() {
  //var sound = document.getElementById("audio");
  //sound.play();
  //disabling for now cause its too laggy.  
}

//This is the AC option, or zeroize the calculation window.
function forClear(){
  document.getElementById('calculation').innerHTML = "0";
  playSound();
}

//You have to clear the zero, not append the zero to the calculation.
function clearZero(){
  var value = document.getElementById('calculation').innerHTML;
  if (value == "0"){
    value = " ";
    document.getElementById('calculation').innerHTML = value;
  }
}

//This funtion adds a parentheses and has some checks for the right or left parentheses
function parentheses(){
  var value = document.getElementById('calculation').innerHTML;
  if((value.match(/\(/g) || []).length > (value.match(/\)/g) || []).length){ //this uses regular expression.
    value = ")";
    document.getElementById('calculation').innerHTML += value;
  }
  else{
    value = "(";
    document.getElementById('calculation').innerHTML += value;
  }
  playSound();
}

var buttons = document.getElementsByTagName('button');

Array.prototype.forEach.call(buttons, function(b){
  b.addEventListener('click', createRipple);
})

//this is an animation effect. 
function createRipple(e)
{
  if(this.getElementsByClassName('ripple').length > 0)
    {
      this.removeChild(this.childNodes[1]);
    }

  var circle = document.createElement('div');
  this.appendChild(circle);

  var d = Math.max(this.clientWidth, this.clientHeight);
  circle.style.width = circle.style.height = d + 'px';

  circle.style.left = e.clientX - this.offsetLeft - d / 2 + 'px';
  circle.style.top = e.clientY - this.offsetTop - d / 2 + 'px';

  circle.classList.add('ripple');
}

//This function is essentially a backspace in the calculation window.
function deleteChar(){
  var value = document.getElementById('calculation').innerHTML;
  value = value.slice(0,value.length - 1);
  document.getElementById('calculation').innerHTML = value;
  playSound();
}

//Just divide the value by 100 and post the value.
function percentage(){
  var value = document.getElementById('calculation').innerHTML;
  value = value.replace(/,/g,'');  //this is needed because % (divide by 100) does not understand commas.
  value = value / 100;
  document.getElementById('calculation').innerHTML = value;
  playSound();
}

//This function adds the value that you input to the display.
function forDisplay(value){
  clearZero();
  document.getElementById('calculation').innerHTML += value;
  playSound();
}

//This uses the eval function in JavaScript. maybe it would be best to solve this myself?
function solveDisplay(){
  clearZero();
  var equation = document.getElementById('calculation').innerHTML;
  equation = equation.replace(/,/g,'');  //this is needed because eval does not understand commas.
  try{
    console.log(equation);
    var solved = eval(equation);
  } catch (e){
    console.log(e);
  }
  //This below adds the commas to the display, sets for English and enables up to 4 decimal digits.
  solved = solved.toLocaleString('en-US', { maximumFractionDigits: 4 });
  document.getElementById('calculation').innerHTML = solved;
  console.log(equation);
  playSound();
}

CSS

The last part of the calculator is the CSS. This is what adds the color and feel of the app. It also adds to the adaptability of the web app on different browsers. I learned the most from this project here. I had never worked with functions in CSS before. This is all saved in a CSS file.

.cal_button {
  flex: 0 25%;
  min-height: 40px;
  height:20%;
}

#outer {
  display: flex;
  flex-flow: column;
  height: 97%;
}

#inner_fixed {
  height: 13em;
}

#footer_fixed{
  height: 3%;
}

#inner_remaining {
  flex: 1;
  display: flex;
  justify-content: center;
  flex-direction: column;
}

#credit {
  font-size: 100%;
  font-style: normal;
  text-align: center;
}

.button {
  color: #black;
  background-position: center;
  transition: background 0.05s;
  background-color: #aac9ed;
  box-shadow: 0 4px #999;
  padding: 10px 10px;
  font-size: 330%;
  width: 100%;
  border-radius: 30px;
  height: 100%;
}

.button:active {
  background-color: #a762c9;
  background-size: 100%;
  transition: background 0s;
}

.display{
  background-color: #e0e1e2;
  min-height: 13em;
  max-height: 20%;
  min-width: 390px;
  border-radius: 18px;
  position:relative;
}

.wholePage{
  background-color: white;
}

.calculation{
  position:relative;
  left: -15px;
  top: 18px;
  font-size: 60px;
  font-style: oblique;
  float: right;
}

.wholeCalculator{
  background-color: black;
  display: flex;
  flex-flow: column;
  height: 100%;
}

.cursor{
  flex: 0 auto;
  font-size: 66px;
  font-style: oblique;
  position:absolute;
  bottom:0;
  right:0;
  animation: blinker 1s linear infinite;
}

/*  This function blinks the cursor in the display.*/
@keyframes blinker {
  50% {
    opacity: 0;
  }
}

.button_container{
  display:flex;
  flex-grow : 1;
  flex-wrap:wrap;
  flex-direction:row;
  padding: 2px;
  max-width: 100%;
  min-width: 390px;
  background-color: #33465b;
  border-radius: 18px;
}

body {
    vertical-align: middle;
    padding:0;
    margin:0;
}

If you want to host this all yourself, I would reference this blog on using Lighttpd and hosting a web app here.

The end product of the calculator web app is calc.powersjo.com.

Check out my previous blog post here.

This post was originally from my blog, referenced below. 

You can get a 25 PRE token bonus if you use my referral code here. This is to support a decentralized web search engine with presearch.org. 

God bless you!

How do you rate this article?


3

0

powersjo
powersjo

Owner of powersjo.com a blogging site talking about books I have read, side projects, IT blogs, and military history.


Powersjo Technical Blog
Powersjo Technical Blog

This is meant to be a collection of technical blogs. The subjects covered are from my own learning and experience in IT.

Send a $0.01 microtip in crypto to the author, and earn yourself as you read!

20% to author / 80% to me.
We pay the tips from our rewards pool.