本文为学习 How to build a calculator—part 1的记录。建议看原文!
先搭个页面结构
看上图,这个计算器分两个主要部分:显示屏、键盘:
<div class="calculator">
<div class="calculator__display">0</div>
<div class="calculator__keys">
</div>
</div>
复制代码
键盘部分有+
、-
、*
、/
等按键:
<div class="calculator__keys">
<button class="keys--operator" data-action="add">+</button>
<button class="keys--operator" data-action="sub">-</button>
<button class="keys--operator" data-action="multi">*</button>
<button class="keys--operator" data-action="div">/</button>
<button>7</button>
<button>8</button>
<button>9</button>
<button>4</button>
<button>5</button>
<button>6</button>
<button>1</button>
<button>2</button>
<button>3</button>
<button>0</button>
<button data-action="decimal">.</button>
<button data-action="clear">AC</button>
<button class="key--equal" data-action="calculate">=</button>
</div>
复制代码
整个页面就这么多东西了~
再加点样式
计算器放在整个网页的中间好看点
body{
height: 100vh;
background-image: linear-gradient(236deg, #74ebd5, #acb6e5);
display: flex;
justify-content: center;
align-items: center;
}
复制代码
再来个圆角,美观
.calculator {
border-radius: 12px;
box-shadow: 0 0 40px 0 rgba(0, 0, 0, 0.15);
overflow: hidden;
}
复制代码
显示器的背景偏黑色,数字是白色的。
.calculator__display {
background-color: #222;
color: #fff;
font-size: 2em;
padding: 0.5em 0.75em;
text-align: right;
}
复制代码
按键用 grid
布局,轻松搞定
.calculator__keys {
background-color: #999;
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-gap: 1px;
}
.calculator__keys>* {
background-color: #fff;
padding: 0.5em 1.25em;
position: relative;
text-align: center;
}
.keys--operator {
background-color: #eee;
}
.key--equal {
background-image: linear-gradient(to bottom, #fe886a, #ff7033);
grid-column: -2;
grid-row: 2/span 4;
}
复制代码
按键在点击的时候应该有按键效果,运算键被按下后也需要有提示
.calculator__keys>*:active::before,
.calculator__keys>.is-depressed::before {
content: "";
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.2);
box-shadow: 0 0 3px 0 rgba(0, 0, 0, 0.5) inset;
position: absolute;
}
复制代码
整个页面就算完成了
开始处理运算逻辑
既然是计算器,肯定要监听点击事件并且判断点击的是哪一个按键。
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')){
const key = e.target;
const action = key.dataset.action;
...
}
})
复制代码
按键从功能上分五种:数字键、运算键、小数点、清除键、求值键。其中运算键、小数点、清除键、求值键都有data-action
属性。没有data-action
属性的就是数字键了。通过这个属性判断被点击的是哪种按键。
const action = key.dataset.action;
if(!action){
console.log('number');
}else if(action === 'add' || action === 'sub' || action === 'multi' || action === 'div'){
console.log('operator');
}else if(action === 'decimal'){
console.log('decimal');
}else if(action === 'clear'){
console.log('clear');
}else if(action==='calculate'){
console.log('calculate');
}
复制代码
一般情况
点击数字键:
点击数字键分两种情况:
- 显示器显示为'0'时按数字键,显示器上显示的数字应该变成所按的数字
- 显示器不为'0'时按数字键,显示器上显示的数字应该是已显示的数字最后一位加上所按的数字
if(!action){
if(display.textContent ==='0'){
display.textContent = key.textContent;
}else {
display.textContent+=key.textContent;
}
}
复制代码
点击运算键
点击运算键后,应该有该键被按下的状态提示
if(action === 'add' || action === 'sub' || action === 'multi' || action === 'div'){
key.classList.add('is-depressed');
}
复制代码
之后再点击其余按键,被按下状态提示应该取消
const action = key.dataset.action;
Array.from(key.parentNode.children).forEach(k => k.classList.remove('is-depressed'));
复制代码
点击小数点
点击小数点后,小数点应该加在显示器显示的数字后面
if(action==='decimal'){
display.textContent +='.';
}
复制代码
但是,假如显示器上已经有小数点了,不应该响应这次点击,所以修改下代码
if(action==='decimal'){
if(!display.textContext.includes('.')){
display.textContext+='.'
}
}
复制代码
点击清除键
AC 键是将计算器所有状态重置。后面会加 CE 键。暂时就是重置显示器:
if(action==='clear'){
display.textContent='0';
}
复制代码
点击计算键
计算键简单,就是算下加减乘除。写个函数好了:
function calculate(firstValue,operator,secondValue){
switch(operator){
case 'add':
return firstValue+secondValue;
case "sub":
return firstValue-secondValue;
case "multi":
return firstValue*secondValue;
case "div":
return firstValue/secondValue;
default:
return "NaN";
}
}
if(action==='calculate'){
display.textContent = calculate(firstValue,operator,secondValue);
}
复制代码
但是,firstValue
,operator
,secondValue
从何而来?
secondValue
就是当前屏幕上的内容,
secondValue = display.textContent;
复制代码
firstValue
、operator
在点击计算键时还存在,趁机将其保存起来
if(action === 'add' || action === 'sub' || action === 'multi' || action === 'div'){
key.classList.add('is-depressed');
calculator.dataset.operator = action;
calculator.dataset.firstValue = display.textContent;
}
复制代码
修改一下点击计算键的代码:
const firstValue = calculator.dataset.firstValue;
const operator = calculator.dataset.operator;
const secondValue = display.textContent;
display.textContent = calculate(firstValue,operator,secondValue);
复制代码
添加清除按钮的重置:
display.textContent='0';
calculator.dataset.firstValue ='';
calculator.dataset.operator = '';
复制代码
至此,一个未处理特殊情况,但功能还算完备的计算器就完成了。剩下的工作就是处理各种组合输入,尽量让计算器没有 BUG。