前不久完成了win10计算器的页面布局(HTML+CSS),这时间我学习了js的一些技术,现在我继续完成其js部分。
为了便于功能显示,在页面布局部分的代码在上次部分做了稍微改动。
完整代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>calculator</title>
<link rel="stylesheet" href="calculator.css">
</head>
<body>
<div class="calculator">
<div class="screen1"><span></span></div> //新增表达式显示屏(初始时无显示)
<div class="screen2"><span>0</span></div>
<div class="M">
<ul>
<li class="m">MC</li>
<li class="m">MR</li>
<li class="c1 m1">M+</li>
<li class="c1 m1">M-</li>
<li class="c1 m1">MS</li>
</ul>
</div>
<div class="row u1">
<ul>
<li class="c1">%</li>
<li class="c1">(</li>
<li class="c1">)</li>
<li class="c1">1/x</li>
</ul>
</div>
<div class="row u1">
<ul>
<li class="c1">CE</li>
<li class="c1">C</li>
<li class="c1">DEL</li>
<li class="c1">/</li>
</ul>
</div>
<div class="row">
<ul>
<li class="c1 u2">7</li>
<li class="c1 u2">8</li>
<li class="c1 u2">9</li>
<li class="c1 u1">*</li>
</ul>
</div>
<div class="row">
<ul>
<li class="c1 u2">4</li>
<li class="c1 u2">5</li>
<li class="c1 u2">6</li>
<li class="c1 u1">-</li>
</ul>
</div>
<div class="row">
<ul>
<li class="c1 u2">1</li>
<li class="c1 u2">2</li>
<li class="c1 u2">3</li>
<li class="c1 u1">+</li>
</ul>
</div>
<div class="row">
<ul>
<li class="c1 u1">+/_</li>
<li class="c1 u2">0</li>
<li class="c1 u1">.</li>
<li class="c1 u1">=</li>
</ul>
</div>
</div>
<script src="calculator.js"></script>//引入js文件
</body>
</html>
* {
margin: 0;
padding: 0;
}
.calculator {
width: 400px;
height: 100%;
background-color: rgb(241, 241, 241);
overflow: hidden;
margin: 0 auto;
//为了美观,适当减少弯角曲率
border-top-left-radius: 10%;
border-top-right-radius: 10%;
}
.screen1 {//新增的显示屏的样式
height: 50px;
line-height: 50px;
margin-top: 50px;
font-size: 20px;
color: yellowgreen;
}
.screen2 {
height: 100px;
font-size: 50px;
line-height: 100px;
color: #000;
font-weight: 600;
}
span {
float: right;
}
.M {
margin-top: 20px;
height: 30px;
}
.M li {
width: 65px;
line-height: 30px;
list-style: none;
float: left;
text-align: center;
font-size: 10px;
}
.m {
color: rgb(192, 192, 192);
}
.m1 {
font-weight: 600;
}
.c1{
transition: all 0.5s;
}
.c1:hover {
background-color: rgb(214, 212, 212);
}
.row {
height: 80px;
}
.row li {
width: 100px;
height: 80px;
line-height: 70px;
list-style: none;
float: left;
text-align: center;
border: 2px solid rgb(241, 241, 241);
box-sizing: border-box;
}
.u1 {
font-size: 20px;
background-color: rgb(245, 245, 245);
}
.u2 {
font-size: 25px;
font-weight: 700;
background-color: rgb(255, 255, 255);
}
(页面布局部分的具体详情可以点击以下链接win10计算器页面布局)
在开始编写js代码前,首先得明确此功能实现中最重要的两个问题:
- 如何找到鼠标点击了哪个li
- 如何把结果传给显示屏
对于问题1的处理方法:通过e.target.innerText得到点击事件的li里面的文本内容,然后通过比较来确定执行相应的功能逻辑。
对于问题2的处理方法:document.getElementsByClassName(className)[0].getElementsByTagName(“span”)[0].innerText,它就可以设置显示屏应要显示的内容。
处理掉这两个问题后,实现功能就易如反掌了。
function my$(className) {
return document.getElementsByClassName(className);
}
var strs = my$("c1");
var flag = false;
for (var i = 0; i < strs.length; i++) {
strs[i].onclick = function (e) {
var str = e.target.innerText;
var tmp = my$("screen1")[0].getElementsByTagName("span")[0].innerText;
if (str === "=") {
var a = calculate(tmp);
//小数显示不下
my$("screen2")[0].getElementsByTagName("span")[0].innerText = a;
flag = true;
} else if (str === "DEL") {
if (flag) {
my$("screen1")[0].getElementsByTagName("span")[0].innerText = "";
flag = false;
} else {
tmp = tmp.substring(0, tmp.length - 1);
my$("screen1")[0].getElementsByTagName("span")[0].innerText = tmp;
}
} else if (str === "C" || str === "CE") {
if (str === "C" || flag) {
my$("screen1")[0].getElementsByTagName("span")[0].innerText = "";
my$("screen2")[0].getElementsByTagName("span")[0].innerText = 0;
flag = false;
} else {
my$("screen2")[0].getElementsByTagName("span")[0].innerText = 0;
}
} else {
if (flag) {
tmp = my$("screen2")[0].getElementsByTagName("span")[0].innerText;
flag = false;
}
if (tmp === "") {
my$("screen1")[0].getElementsByTagName("span")[0].innerText = str;
} else {
my$("screen1")[0].getElementsByTagName("span")[0].innerText = tmp + str;
}
}
}
}
//算数算法
var calculate = function (s) {
var stack = new Array();
for (var i = 0; i < s.length; i++) {
var tmp = s[i];
if (tmp >= '0' && tmp <= '9') {
var r = getNum(s, i);
dealNum(s, stack, r.num);
i = r.index;
} else if (tmp === '+' || tmp === '-' || tmp === '*' || tmp === '/' || tmp === '(') {
stack.push(tmp);
} else if (tmp === ')') {
dealRight(s, stack);
}
}
return getFinalResult(stack);
};
var getNum = function (s, i) {
var start = i;
while (i < s.length && ((s[i] >= '0' && s[i] <= '9') || s[i] === '.')) {
i++;
}
return {
num: Number(s.substring(start, i)),
index: i - 1
};
};
var dealNum = function (s, stack, num) {
if (stack.length !== 0 && (stack[stack.length - 1] === '*' || stack[stack.length - 1] === '/')) {
var sign1 = stack.pop();
var pre1 = stack.pop();
var res = sign1 === '*' ? pre1 * num : pre1 / num;
stack.push(res);
} else {
stack.push(num);
}
}
var dealRight = function (s, stack) {
var result = 0;
//括号里面只有+/-运算
while (stack[stack.length - 1] !== '(') {
var next1 = stack.pop();
if (stack.length === 0 || stack[stack.length - 1] === '(') {
result += next1;
break;
}
var sign2 = stack.pop();
result = sign2 === '+' ? result + next1 : result - next1;
}
stack.pop();
var res2 = 0;
//使得stack里面只有+/-运算符,保证算法优先级(原因:完成括号运算后的结果必然是一个数字)
while (stack.length !== 0 && (stack[stack.length - 1] === '*' || stack[stack.length - 1] === '/')) {
var sign3 = stack.pop();
var pre2 = stack.pop();
res2 = sign3 === '*' ? pre2 * result : pre2 / result;
result = res2;
}
stack.push(result);
}
var getFinalResult = function (stack) {
var result = 0;
while (stack.length > 0) {
var next = stack.pop();
if (stack.length === 0) {
return result + next;
}
var sign = stack.pop();
result = sign === '+' ? result + next : result - next;
}
return result;
};
解释几个地方:
- 由于显示屏的大小以及字体的大小设置不合理而产生如下情况的bug(interesting)
- 这里面还有部分功能未实现(包括部分点击事件,算数算法没有支持正负数以及异常输入和异常结果的处理)
- 算法部分我采用的是自己编写的算数算法,如果有不懂可以看leetcode772题–基本计算器 III(这题是困难难度,其实就是1,2的综合版),此算法的思路来源 算法思路。