[TIL] Javascript assignment(Web) : May 8, 2020

RE_BROTHER·2020년 5월 8일
0

TIL

목록 보기
10/41

Calculator

CSS

CSS는 이전에 작업했던 CSS를 참조하여 작성하였고, 계산기에 맞게 적절히 조절했다.

/*
Date : 2020-05-08
Time : 10:22
File : style.css
Dir : D:\dev\calc
*/

html {
  box-sizing: border-box;
}

*,
*::before,
*::after {
  box-sizing: inherit;
}

body {
  margin: 0;
}

/* Responsive Images */

embed,
iframe,
img,
object,
video {
  max-width: 100%;
}

h1,
h2,
h3,
h4,
h5,
h6,
ul,
ol,
li,
p,
pre,
blockquote,
figure,
hr {
  margin: 0;
  padding-right: 0;
  padding-left: 0;
}

a {
  text-decoration: none;
}

a:focus {
  outline: none;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  display: block;
}

/* Removes all decimals and discs from lists */

ol,
ul {
  list-style: none;
}

input,
textarea,
button {
  border: 0;
  border-radius: 0;
  background-color: transparent;
  font-size: inherit;
  font-family: inherit;
  font-weight: inherit;
  outline: none;
  appearance: none;
  text-align: left;
}

input:hover,
input:active,
input:focus,
textarea:hover,
textarea:active,
textarea:focus,
button:hover,
button:active,
button:focus {
  outline: none;
}

:root {
  font-family: Helvetica, Arial, sans-serif;
}

html {
  font-size: 175%;
  font-weight: 300;
  line-height: 1.3;
}

body {
  align-items: center;
  background-image: linear-gradient(236deg, #272f38, #898999);
  display: flex;
  height: 100vh;
  justify-content: center;
}

.container {
  max-width: 20em;
  color: white
}

.container > p {
  text-align: center;
}

.calculator {
  border-radius: 12px;
  box-shadow: 0 0 40px 0px rgba(0, 0, 0, 0.15);
  margin-left: auto;
  margin-right: auto;
  margin-top: 2em;
  max-width: 15em;
  overflow: hidden;
}

.calculator__display {
  background-color: #272f38;
  color: #fff;
  font-size: 1.714285714em;
  padding: 0.5em 0.75em;
  text-align: right;
}

.calculator__keys {
  background-color: #999;
  display: grid;
  grid-gap: 1px;
  grid-template-columns: repeat(4, 1fr);
}

.calculator__keys > * {
  background-color: #fff;
  padding: 0.5em 1.25em;
  position: relative;
  text-align: center;
}

.calculator__keys > *:active::before,
.calculator__keys > .is-depressed::before {
  background-color: rgba(0, 0, 0, 0.2);
  bottom: 0;
  box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.5) inset;
  content: "";
  left: 0;
  opacity: 0.3;
  position: absolute;
  right: 0;
  top: 0;
  z-index: 1;
}

.key--operator {
  background-color: #eee;
}

.key--equal {
  background-image: linear-gradient(to bottom, #272f38, #484f59);
  grid-column: -2;
  grid-row: 2 / span 4;
}

HTML

현재 기능 구현을 하지 않은 껍데기만 있는 계산기의 모습이다.
style.css를 html 코드에서 link하여 사용중이다.
link href="style.css" rel="stylesheet"

Code

<!--
Date : 2020-05-07
Time : 11:01
File : calculator.html
Dir : D:\dev\calc
-->


<link href="style.css" rel="stylesheet">
<script src="calculator.js"></script>

<div class="container">
    <p>
      RE_BROTHER CALCULATOR
    </p>
    <div class="calculator">
      <div class="calculator__display">0</div>

      <div class="calculator__keys">
        <button>7</button>
        <button>8</button>
        <button>9</button>
        <button data-action="clear">AC</button>
        <button>4</button>
        <button>5</button>
        <button>6</button>
        <button>1</button>
        <button>2</button>
        <button>3</button>
        <button data-action="decimal">.</button>
        <button>0</button>
        <button class="key--equal" data-action="calculate">=</button>
        <button class="key--operator" data-action="add">+</button>
        <button class="key--operator" data-action="sub">-</button>
        <button class="key--operator" data-action="mul">&times;</button>
        <button class="key--operator" data-action="div">÷</button>
      </div>
    </div>
  </div>

Javascript

웹 페이지에서 사용하는 계산기가 아닌 terminal 상에서 사용하는 계산기에 대한 코드를 미리 작성해보았다.

const calculate = (n1, operator, n2) => {
    let result = ''
    if (operator === 'add') {
        result = parseFloat(n1) + parseFloat(n2)
    } else if (operator === 'subtract') {
        result = parseFloat(n1) - parseFloat(n2)
    } else if (operator === 'multiply') {
        result = parseFloat(n1) * parseFloat(n2)
    } else if (operator === 'divide') {
        result = parseFloat(n1) / parseFloat(n2)
    }
    return result
}

위 코드를 실행하면 operator에 따라 연산 기호가 변경된다. 하지만 위와 같이 코드를 작성하게 되면 재할당이 너무 많이 되기 때문에 재할당을 줄여야할 필요성이 있어서 아래와 같이 수정해보았다.

const calculate = (n1, operator, n2) => {
  if (operator === 'add') {
    return firstNum + parseFloat(n2)
  } else if (operator === 'subtract') {
    return parseFloat(n1) - parseFloat(n2)
  } else if (operator === 'multiply') {
    return parseFloat(n1) * parseFloat(n2)
  } else if (operator === 'divide') {
    return parseFloat(n1) / parseFloat(n2)
  }
}

이제 직접 계산기에 들어갈 js파일을 생성하여 하나 하나 기능 구현을 진행해보도록 하겠다.

Code

/*
Date : 2020-05-08
Time : 13:00
File : calculator.js
Dir : D:\dev\calc
*/

const calculate = (n1, operator, n2) => {
  const firstNum = parseFloat(n1)
  const secondNum = parseFloat(n2)
  if (operator === 'add') return firstNum + secondNum
  if (operator === 'sub') return firstNum - secondNum
  if (operator === 'mul') return firstNum * secondNum
  if (operator === 'div') return firstNum / secondNum
}

const getKeyType = key => {
  const { action } = key.dataset
  if (!action) return 'number'
  if (
    action === 'add' ||
    action === 'sub' ||
    action === 'mul' ||
    action === 'div'
  ) return 'operator'
  
  return action
}

const createResultString = (key, displayedNum, state) => {
  const keyContent = key.textContent
  const keyType = getKeyType(key)
  const {
    firstValue,
    operator,
    modValue,
    previousKeyType
  } = state

  if (keyType === 'number') {
    return displayedNum === '0' ||
      previousKeyType === 'operator' ||
      previousKeyType === 'calculate'
      ? keyContent
      : displayedNum + keyContent
  }

  if (keyType === 'decimal') {
    if (!displayedNum.includes('.')) return displayedNum + '.'
    if (previousKeyType === 'operator' || previousKeyType === 'calculate') return '0.'
    return displayedNum
  }

  if (keyType === 'operator') {
    return firstValue &&
      operator &&
      previousKeyType !== 'operator' &&
      previousKeyType !== 'calculate'
      ? calculate(firstValue, operator, displayedNum)
      : displayedNum
  }

  if (keyType === 'clear') return 0

  if (keyType === 'calculate') {
    return firstValue
      ? previousKeyType === 'calculate'
        ? calculate(displayedNum, operator, modValue)
        : calculate(firstValue, operator, displayedNum)
      : displayedNum
  }
}

const updateCalculatorState = (key, calculator, calculatedValue, displayedNum) => {
  const keyType = getKeyType(key)
  const {
    firstValue,
    operator,
    modValue,
    previousKeyType
  } = calculator.dataset

  calculator.dataset.previousKeyType = keyType

  if (keyType === 'operator') {
    calculator.dataset.operator = key.dataset.action
    calculator.dataset.firstValue = firstValue &&
      operator &&
      previousKeyType !== 'operator' &&
      previousKeyType !== 'calculate'
      ? calculatedValue
      : displayedNum
  }

  if (keyType === 'calculate') {
    calculator.dataset.modValue = firstValue && previousKeyType === 'calculate'
      ? modValue
      : displayedNum
  }

  if (keyType === 'clear' && key.textContent === 'AC') {
    calculator.dataset.firstValue = ''
    calculator.dataset.modValue = ''
    calculator.dataset.operator = ''
    calculator.dataset.previousKeyType = ''
  }
}

const updateVisualState = (key, calculator) => {
  const keyType = getKeyType(key)
  Array.from(key.parentNode.children).forEach(k => k.classList.remove('is-depressed'))

  if (keyType === 'operator') key.classList.add('is-depressed')
  if (keyType === 'clear' && key.textContent !== 'AC') key.textContent = 'AC'
  if (keyType !== 'clear') {
    const clearButton = calculator.querySelector('[data-action=clear]')
    clearButton.textContent = 'CE'
  }
}

const calculator = document.querySelector('.calculator')
const display = calculator.querySelector('.calculator__display')
const keys = calculator.querySelector('.calculator__keys')

keys.addEventListener('click', e => {
  if (!e.target.matches('button')) return
  const key = e.target
  const displayedNum = display.textContent
  const resultString = createResultString(key, displayedNum, calculator.dataset)

  display.textContent = resultString
  updateCalculatorState(key, calculator, resultString, displayedNum)
  updateVisualState(key, calculator)
})

Test URL : https://codepen.io/re_brother/pen/zYvWpwP

profile
I hope the All-Rounder Developer & Researcher

0개의 댓글