순수 자바스크립트를 이용해서 만드는 계산기입니다.
깃헙 전체 코드 확인
위 그림에서 동작과정과 같이 입력, 계산 과정, 임시 결과를 출력해주는 계산기를 만들기 위해 필요한 과제
+@ 키보드 입력
<div class="wrap">
<div class="calc">
<div class="display-container">
<div class="display-1">0</div>
<div class="display-2">0</div>
<div class="temp-result">0</div>
</div>
<button class="button all-clear">AC</button>
<button class="button operation">±</button>
<button class="button operation">%</button>
<button class="button operation">÷</button>
<button class="button number">7</button>
<button class="button number">8</button>
.
.
.
</div>
</div>
html에서 중요한 부분은 display-container 부분입니다.
위 과제의 입력창(결과창), 임시 결과창, 계산 과정창을 출력하기 위해 3개의 출력창이 필요합니다.
숫자와 기호를 다른 클레스명으로 묶어서 사용하면 js 코드에서 편하게 할 수 있습니다.
기호의 경우 아이콘을 사용하지 않고 유니코드
를 사용하면 편합니다.
유니코드 👈
스타일은 본인의 취향에 맞게 적용합니다.
저는 아이폰 계산기 디자인을 참고했습니다.
.calc
에 display: grid
속성을 주면 정렬을 편하게 할 수 있습니다.grid-column: span 4
속성 값을 적용하면 한줄 전체를 사용하게 만들 수 있습니다.grid-column: span 2
하게 만들 수 있습니다.EXAMPLE 👇
.calc {
display: grid;
grid-template-columns: repeat(4, 1fr);
}
.display-container {
grid-column: span 4;
}
가장 먼저 필요한 태그를 불러옵니다.
const display1El = document.querySelector(".display-1");
const display2El = document.querySelector(".display-2");
const tempResultEl = document.querySelector(".temp-result");
const numbersEl = document.querySelectorAll(".number");
const operationEl = document.querySelectorAll(".operation");
const equalEl = document.querySelector(".equal");
const clearAllEl = document.querySelector(".all-clear");
출력에 필요한 태그 3가지와 숫자가 들어있는 태그들 기호가 들어있는 태그들 그리고 특별한 기능을 하는 태그 두가지를 불러옵니다.
다음은 필요한 변수를 전역에 선언합니다.
// 출력할 값을 담을 변수
let display1Num = "";
let display2Num = "";
let result = null;
// 기호를 담을 변수
let lastOperation = "";
// . 중복 체크를 위한 변수
let haveDot = false;
가장 먼저 .의 중복 체크와 클릭한 숫자를 변수에 넣고 변수 값을 출력하는 부분입니다.
// number click Event
numbersEl.forEach(number => {
number.addEventListener("click", (e) => {
// .의 중복 체크 부분
if(e.target.innerText === "." && !haveDot) {
haveDot = true;
} else if(e.target.innerText === "." && haveDot) {
return;
}
// 클릭한 숫자값 변수에 넣고 변수 값을 출력하는 부분
display2Num += e.target.innerText;
display2El.innerText = display2Num;
})
})
먼저 .의 중복체크가 필요하며 체크가 끝난 값을 출력하는 과정입니다.
다음은 기호 클릭시 일어날 수 있는 경우를 체크합니다.
// operation click Event
operationEl.forEach(operation => {
operation.addEventListener("click", (e) => {
// 기호 앞에 아무값도 없을 경우 아무것도 실행할 수 없음
if(!display2Num) {
return;
}
// 기호 다음 두번째 값은 .을 다시 사용할 수 있어야함
haveDot = false;
// 클릭한 기호를 변수에 넣음
const operationSymbol = e.target.innerText;
// 첫번째 값과 두번째 값 그리고 기호가 모두 있을 때
if(display1Num && display2Num && lastOperation) {
// 계산 실행
mathOperation();
} else {
// 하나라도 없을 경우 입력값을 임수 출력 결과창에 사용할 변수에 숫자로 변환 후 담아둠
result = parseFloat(display2Num);
}
clearVar(operationSymbol);
lastOperation = operationSymbol;
})
})
먼저 첫번째 값 즉, 기호 앞에 아무값도 없을 경우 아무것도 실행할 수 없습니다.
[다만 이 경우에 첫번째 값에 음수를 넣을 수 없는 오류가 있습니다.]
// 첫번째 입력 숫자와 기호를 합쳐 출력하는 함수 및 임시 결과창에 값 출력
function clearVar(symbol = "") {
display1Num += `${display2Num} ${symbol} `;
display1El.innerText = display1Num;
display2El.innerText = "";
display2Num = "";
// 임시 결과창에 저장한 값 출력
tempResultEl.innerText = result;
}
위 코드를 Arrow functions(화살표 함수)로 변경해도 상관없습니다.
Template Literals(템플릿 리터럴) 👉
${display2Num} ${symbol}
문자열을 연결하기 위해 + 연산자를 사용하기 보단 es6의 템플릿 리터널을 사용하는 것이 더 깔끔하고 보기 좋은 코드를 만들 수 있습니다.
Default parameters(기본 매개 변수) 👉clearVar(symbol = "")
매개 변수를 사용하지 않을 경우 미리 기본값을 정의할 수 있으며, 누락된 매개 변수를 사용해도 함수를 실행할 때 오류를 반환하지 않습니다.
// 모든 값이 있을 경우 각 기호별로 계산 및 임시 결과창에 계산 결과 출력
function mathOperation() {
if(lastOperation === "×") {
result = parseFloat(result) * parseFloat(display2Num);
} else if(lastOperation === "+") {
result = parseFloat(result) + parseFloat(display2Num);
} else if(lastOperation === "−") {
result = parseFloat(result) - parseFloat(display2Num);
} else if(lastOperation === "÷") {
result = parseFloat(result) / parseFloat(display2Num);
} else if(lastOperation === "%") {
result = parseFloat(result) % parseFloat(display2Num);
}
}
각 기호별 계산을 진행합니다.
[±의 계산은 따로 정의하지 않았습니다.]
특수 기호 클릭시 발생하는 이벤트를 정의합니다.
// equalEl click Event
equalEl.addEventListener("click", (e) => {
// 첫번째 값 혹은 두번째 값이 없을 경우는 계산 불가하여 아무것도 하지 않음
if(!display1Num || !display2Num) {
return;
}
// 둘다 값이 있을 경우
haveDot = false;
mathOperation();
clearVar();
display2El.innerText = result;
tempResultEl.innerText = "";
display2Num = result;
display1Num = "";
})
// all clear(AC) click Event
clearAllEl.addEventListener("click", (e) => {
// 모든 값 초기화
display1El.innerText = "0";
display2El.innerText = "0";
display1Num = "";
display2Num = "";
result = "";
tempResultEl.innerText = "0";
haveDot = false;
})
키보드를 이용해 조작할 수 있는 방법입니다.
window.addEventListener("keydown", (e) => {
// 누른 키의 값을 받아와 각 종류 별 매칭되는 클릭과 연결
if(
e.key === "0" ||
e.key === "1" ||
e.key === "2" ||
e.key === "3" ||
e.key === "4" ||
e.key === "5" ||
e.key === "6" ||
e.key === "7" ||
e.key === "8" ||
e.key === "9" ||
e.key === "."
) {
clickButtonEl(e.key);
} else if(
e.key === "+" ||
e.key === "/" ||
e.key === "%"
) {
clickOperation(e.key);
} else if(e.key === "*") {
clickOperation("×");
} else if(e.key === "-") {
clickOperation("−");
} else if(e.key === "?") {
clickOperation("÷");
} else if(e.key === "Enter" || e.key === "=") {
ClickEqual();
}
});
/의 경우 키입력이 안되는 오류가 있어 동일한 키인 ?로 대채했습니다.
// 숫자 클릭
function clickButtonEl(key) {
numbersEl.forEach(button => {
if(button.innerText === key) {
button.click();
}
})
};
// 기호 클릭
function clickOperation(key) {
operationEl.forEach(button => {
if(button.innerText === key) {
button.click();
}
})
};
function ClickEqual() {
equalEl.click();
};
keydown 이벤트로 일어나는 키입력과 클릭을 연동시켜서 작동하는 방식입니다.