DEV Community

benboorstein
benboorstein

Posted on • Edited on

2 1

Brian Holt's Calculator with Walk-through of '1 + 2 = 3'

  <body>
    <div class="calc">
      <section class="screen">
        0
      </section>
      <section class="calc-buttons">
        <div class="calc-row">
          <button class="double calc-button">C</button>
          <button class="calc-button"></button>
          <button class="calc-button">÷</button>
        </div>
        <div class="calc-row">
          <button class="calc-button">7</button>
          <button class="calc-button">8</button>
          <button class="calc-button">9</button>
          <button class="calc-button">×</button>
        </div>
        <div class="calc-row">
          <button class="calc-button">4</button>
          <button class="calc-button">5</button>
          <button class="calc-button">6</button>
          <button class="calc-button">-</button>
        </div>
        <div class="calc-row">
          <button class="calc-button">1</button>
          <button class="calc-button">2</button>
          <button class="calc-button">3</button>
          <button class="calc-button">+</button>
        </div>
        <div class="calc-row">
          <button class="triple calc-button">0</button>
          <button class="calc-button">=</button>
        </div>
      </section>
    </div>
    <script src="./assets/js/app.js"></script>
  </body>
Enter fullscreen mode Exit fullscreen mode
* {
    box-sizing: border-box;
}

body {
    padding: 0;
    margin: 0;
}

.calc {
    width: 400px;
    background-color: black;
    color: white;
}

.screen {
    font-size: 40px;
    font-family: 'Courier New', Courier, monospace;
    text-align: right;
    padding: 20px 5px;
}

.calc-button {
    background-color: #d8d9db;
    color: black;
    height: 100px;
    width: 24.5%;
    border: none;
    border-radius: 0;
    font-size: 40px;
    cursor: pointer;
}

.calc-button:hover {
    background-color: #ebebeb;
}

.calc-button:active {
    background-color: #bbbcbe;
}

.calc-button:last-child {
    background-color: #df974c;
    color: white;
}

.calc-button:last-child:hover {
    background-color: #dfb07e;
}

.calc-button:last-child:active {
    background-color: #dd8d37;
}

.double {
    width: 49.7%;
}

.triple {
    width: 74.8%;
}

.calc-row {
    display: flex;
    justify-content: space-between;
    align-content: stretch;
    margin-bottom: 0.5%;
}
Enter fullscreen mode Exit fullscreen mode
// The below code is from one of Brian Holt's Frontend Masters courses. The comments are mine, added to help me better understand the calculator.


// WALKING THROUGH '1 + 2 = 3':

  // BEFORE ANYTHING IS CLICKED:
    // runningTotal = 0
    // previousOperator = null
    // buffer = '0'

  // AFTER HAVING CLICKED '1':
    // runningTotal = 0
    // previousOperator = null
    // buffer = '1'

  // AFTER HAVING CLICKED '1 +':
    // runningTotal = 1
    // previousOperator = +
    // buffer = '0'

  // AFTER HAVING CLICKED '1 + 2':
    // runningTotal = 1
    // previousOperator = +
    // buffer = '2'

  // AFTER HAVING CLICKED '1 + 2 =':
    // runningTotal = 0
    // previousOperator = null
    // buffer = '3'

let runningTotal = 0 // numerical total (not seen in UI)
let buffer = '0' // what user is typing in ('buffer' gets stored below in 'screen.innerText' so is effectively what's seen in the UI)
let previousOperator = null // most recent operator pressed // note that 'null' is different than '0'; 'null' is the absence of anything
const screen = document.querySelector('.screen')

document.querySelector('.calc-buttons').addEventListener('click', function(event) { // using event delegation/bubbling here
  buttonClick(event.target.innerText)
})

function buttonClick(value) { 
  if (isNaN(parseInt(value))) { // MDN: 'isNaN()' determines if a value is NaN or not. Because coercion inside 'isNaN()' can be surprising, 'Number.isNaN()' may be preferable
    handleSymbol(value)
  } else {
    handleNumber(value)
  }
  rerender()
}

function handleNumber(value) {
  if (buffer === '0') {
    buffer = value
  } else {
    buffer += value
  }
}

function handleSymbol(value) {
  switch (value) { // using a 'switch' statement instead of an 'if...else' statement with a bunch of 'else if'
    case 'C': // i.e., if 'value' is 'C', do what's below
      buffer = '0'
      runningTotal = 0 // yes, this IS here for a reason. E.g., if user pressed '2' and then '+', runningTotal would have '2' stored in it, so if user decided at this point to press 'C' to clear, then runningTotal would need to be set back to 0
      previousOperator = null
      break
    case '=':
      if (previousOperator === null) {
        return // do nothing, and exit this function
      }
      flushOperation(parseInt(buffer))
      previousOperator = null
      buffer = '' + runningTotal // concatenates the empty string and 'runningTotal', thereby storing in 'buffer' 'runningTotal''s value but as a string
      runningTotal = 0
      break
    case '':
      if (buffer.length === 1) {
        buffer = '0'
      } else {
        buffer = buffer.substring(0, buffer.length - 1)
      }
      break
    default:
      handleMath(value)
      break
  }
}

function handleMath(value) {
  const intBuffer = parseInt(buffer)
  if (runningTotal === 0) {
    runningTotal = intBuffer
  } else {
    flushOperation(intBuffer)
  }

  previousOperator = value

  buffer = '0' // this is the reason '0' appears each time '÷', '×', '-', or '+' is pressed
}

function flushOperation(intBuffer) { // this is a confusing name for this parameter, given that 'intBuffer' is the name of an actual variable and argument above.
  if (previousOperator === '+') {
    runningTotal += intBuffer
  } else if (previousOperator === '-') {
    runningTotal -= intBuffer
  } else if (previousOperator === '×') {
    runningTotal *= intBuffer
  } else {
    runningTotal /= intBuffer
  }
}

function rerender() {
  screen.innerText = buffer
}
Enter fullscreen mode Exit fullscreen mode

Image of AssemblyAI tool

Transforming Interviews into Publishable Stories with AssemblyAI

Insightview is a modern web application that streamlines the interview workflow for journalists. By leveraging AssemblyAI's LeMUR and Universal-2 technology, it transforms raw interview recordings into structured, actionable content, dramatically reducing the time from recording to publication.

Key Features:
🎥 Audio/video file upload with real-time preview
🗣️ Advanced transcription with speaker identification
⭐ Automatic highlight extraction of key moments
✍️ AI-powered article draft generation
📤 Export interview's subtitles in VTT format

Read full post

Top comments (0)

AWS Security LIVE!

Tune in for AWS Security LIVE!

Join AWS Security LIVE! for expert insights and actionable tips to protect your organization and keep security teams prepared.

Learn More

👋 Kindness is contagious

Dive into an ocean of knowledge with this thought-provoking post, revered deeply within the supportive DEV Community. Developers of all levels are welcome to join and enhance our collective intelligence.

Saying a simple "thank you" can brighten someone's day. Share your gratitude in the comments below!

On DEV, sharing ideas eases our path and fortifies our community connections. Found this helpful? Sending a quick thanks to the author can be profoundly valued.

Okay