引言
这是一篇很棒的文章,在这里你可以学习如何从零做出一款计算器。我们希望你使用 JavaScript 开发并且思考怎么构建一款计算器, 如何编写代码,以及最后,如何整理自己的代码。
在这篇文章结束,你会得到一款和 iPhone 计算器功能一样的计算器(除了 +/- 和百分比功能外)。
前置条件
在你开始本节课程前,请确保你对 JavaScript 有一个不错的了解。最起码,你需要知道以下事情:
- If/else 分支
- For 循环
- JavaScript 函数
- 箭头函数
&&
和||
操作符- 如何使用
textContent
属性修改文本 - 如何使用事件代理模式添加事件
开始之前
我建议你在开始课程之前自己尝试下自己开发计算器。这是一个很好的锻炼,因为你会训练自己像开发人员一样思考。
一旦你尝试了一小时,再回来上这节课(不管你是成功还是失败。当年尝试过,思考过,这会帮助你在更短的时间内吸收本节课的内容)。
就这样,我们先来了解下计算器的工作原理。如果对Python有兴趣,想了解更多的Python以及AIoT知识,解决测试问题,以及入门指导,帮你解决学习Python中遇到的困惑,我们这里有技术高手。如果你正在找工作或者刚刚学校出来,又或者已经工作但是经常觉得难点很多,觉得自己Python方面学的不够精想要继续学习的,想转行怕学不会的, 都可以加入我们,可领取最新Python大厂面试资料和Python爬虫、人工智能、学习资料!VX【pydby01】暗号CSDN
构建计算器
首先,我们想要建立计算器。
这个计算机包含两个部分:显示屏和键盘。
<div class=”calculator”>
<div class=”calculator__display”>0</div>
<div class=”calculator__keys”> … </div>
</div>
我们使用 CSS Grid 去制作键盘部分,因为他们是类似网格的格式进行排列的。这里已经在启动文件中完成了,你可以在以下地址找到启动文件 此处.
.calculator__keys {
display: grid;
/* other necessary CSS */
}
为了帮助我们区分操作符,小数点,清除符号以及等号,我们将设置一个data-action
属性用来描述他们的功能。
<div class="calculator__keys">
<button class="key--operator" data-action="add">+</button>
<button class="key--operator" data-action="subtract">-</button
<button class="key--operator" data-action="multiply">×</button>
<button class="key--operator" data-action="divide">÷</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>
监听键盘点击
当一个人拿着几个计算器,他会做五种事情,他们可以点击:
- 一个数字键(0-9)
- 一个操作键 (+,-,×,÷)
- 小数点键
- 等号键
- 清除键
构建这个计算器的第一步是能够监听所有(1)的按键,确定(2)被按下时候的类型。在这个案例中,我们可以使用事件代理模式去监听,因为所有的按键都是.calculator__keys
的孩子。
const calculator = document.querySelector(‘.calculator’)
const keys = calculator.querySelector(‘.calculator__keys’)
keys.addEventListener(‘click’, e => {
if (e.target.matches(‘button’)) {
// Do something
}
})
接下来,我们利用data-action
属性去确定点击按键的类型。
const key = e.target
const action = key.dataset.action
如果按键没有data-action
属性,那么它一定是一个数字键。
if (!action) {
console.log('number key!')
}
如果这个按键有data-action
,它的值是 add
,subtract
,multiply
或者divide
,我们就可以知道这是一个操作按键。
if (
action === 'add' ||
action === 'subtract' ||
action === 'multiply' ||
action === 'divide'
) {
console.log('operator key!')
}
如果这个按键的data-action
属性是decimal
,我们就可以知道使用者点击了小数点键。
按照同样的思路,如果键的data-action
是clear
,我们知道用户点击了清除(写着 AC 的那个)键。如果键的data-action
是calculate
,我们知道用户点击了等于键。
if (action === 'decimal') {
console.log('decimal key!')
}
if (action === 'clear') {
console.log('clear key!')
}
if (action === 'calculate') {
console.log('equal key!')
}
在这里,你可以使用console.log
方法,来响应每个按键的事件。
开始构建 happy path
让我们思考一下,一个普通人拿到一个计算器之后,会做什么呢?这个普通人会做什么的问题被称作 happy path。
这个普通人我们就称作 Mary 吧。
当 Mary拿起计算器时,她可能会点击任何一个按键:
- 一个数字键(0-9)
- 一个操作键 (+,-,×,÷)
- 小数点键
- 等号键
- 清除键
一下子要思考五种按键可以能不太容易,所以让我们一步一步来。
当使用者按下数字键
如果计算器显示 0(默认数字),此时,目标数字需要替换这个 0。
如果计算器显示的是非零数字,那么目标数字就需要在显示的数字后面添加上。
现在,我们需要知道两件事情:
- 当前被点击的按键的数字。
- 当前显示的数字。
我们可以通过textContent
和点击按键的.calculator__display
分别获取到这两个值。
const display = document.querySelector('.calculator__display')
keys.addEventListener('click', e => {
if (e.target.matches('button')) {
const key = e.target
const action = key.dataset.action
const keyContent = key.textContent
const displayedNum = display.textContent
// ...
}
})
如果计算器显示0,我们需要用点击按键的数字替换计算器显示屏的数字。 我们可以通过显示屏的textContent
属性进行替换。
if (!action) {
if (displayedNum === '0') {
display.textContent = keyContent
}
}
如果计算器显示的是非零数字,我们需要在当前显示的数字后面追加点击键的数字。 要追加一个数字,我们就需要一个连接字符串。
if (!action) {
if (displayedNum === '0') {
display.textContent = keyContent
} else {
display.textContent = displayedNum + keyContent
}
}
这时,Mary 可能会点击其中一个按键:
- 小数点键
- 操作符键
让我们告诉 Mary 点击一下小数点键吧。
当使用者点击小数点键时
当 Mary 点击了小数点键之后,小数点就需要出现在显示屏上。如果 Mary 在敲击小数键后敲击任何数字,那么数字也应该添加在显示屏上。
为了实现上述效果,我们需要将.
添加到已经显示的数字后面。如果对Python有兴趣,想了解更多的Python以及AIoT知识,解决测试问题,以及入门指导,帮你解决学习Python中遇到的困惑,我们这里有技术高手。如果你正在找工作或者刚刚学校出来,又或者已经工作但是经常觉得难点很多,觉得自己Python方面学的不够精想要继续学习的,想转行怕学不会的, 都可以加入我们,可领取最新Python大厂面试资料和Python爬虫、人工智能、学习资料!VX【pydby01】暗号CSDN
if (action === 'decimal') {
display.textContent = displayedNum + '.'
}
接下来,我们可以让 Mary 继续点击计算器的操作按键继续她的计算。
当使用者点击操作按钮
如果 Mary 点击操作按键,这个操作符需要被高亮,这样的话 Mary 就知道了这个操作符是激活的。
为了实现这个功能,我们给操作符按钮添加一个名字叫is-depressed
的类名。
if (
action === 'add' ||
action === 'subtract' ||
action === 'multiply' ||
action === 'divide'
) {
key.classList.add('is-depressed')
}
一旦 Mary 按下了一个操作键,她将会点击另外的数字键。
当使用者在点击了操作键后点击了数字键
当 Mary 再次点击了数字键,之前显示的数字应该被替换成新的数组。操作键也应该被解除“被点击”的状态。
我们可以使用forEach
循环遍历所有的按键,去移除is-depressed
类:
keys.addEventListener('click', e => {
if (e.target.matches('button')) {
const key = e.target
// ...
// Remove .is-depressed class from all keys
Array.from(key.parentNode.children)
.forEach(k => k.classList.remove('is-depressed'))
}
})
接下来,我们想要把显示的内容更新为之前点击过的按键。在我们做这件事之前,我们需要判断之前的按键是否是一个操作键。
我们可以通过自定义属性来实现。让我们定义一个自定义属性data-previous-key-type
。
const calculator = document.querySelector('.calculator')
// ...
keys.addEventListener('click', e => {
if (e.target.matches('button')) {
// ...
if (
action === 'add' ||
action === 'subtract' ||
action === 'multiply' ||
action === 'divide'
) {
key.classList.add('is-depressed')
// Add custom attribute
calculator.dataset.previousKeyType = 'operator'
}
}
})
If the previousKeyType
is an operator, we want to replace the displayed number with clicked number.
如果previousKeyType
是一个操作符,我们希望可以用当前点击的数字替换当前显示的数字。
const previousKeyType = calculator.dataset.previousKeyType
if (!action) {
if (displayedNum === '0' || previousKeyType === 'operator') {
display.textContent = keyContent
} else {
display.textContent = displayedNum + keyContent
}
}
接下来让我们告诉 Mary 点击等号键来完成她的计算。
当使用者点击等号键时
当 Mary 点击等号键,计算器应该根据三个值计算一个结果:
- 第一个输入计算器中的数字
- 操作符
- 第二个输入计算器中的数字
在计算之后,结果会替换当前已显示的值出现在屏幕上。
这里我们只知道第二个数字是当前已经显示的数字。
if (action === 'calculate') {
const secondValue = displayedNum
// ...
}
为了获取第一个数字,我们需要储存之前在计算器上被我们已经清除了的值。我们可以添加一个自定义的属性,在我们点击操作键是储存第一个值。
获取操作符,我们可以使用同样的方法。
if (
action === 'add' ||
action === 'subtract' ||
action === 'multiply' ||
action === 'divide'
) {
// ...
calculator.dataset.firstValue = displayedNum
calculator.dataset.operator &#