想尝试做一个网页版计算器后,参考了很多博客大神的代码,综合归纳、总结修改,整理如下文。
一、HTML+CSS
具体结构样式如下图,基本参照手机计算器界面。按钮功能可以查看demo,都可以实现。
这部分布局不是重点,只附上代码以便理解JS行为。
HTML/结构:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>JS实现轻量级计算器</title> <link href="css/style.css" type="text/css" rel="stylesheet" /> <script src="js/script.js" type="text/javascript" rel="stylesheet"></script> </head> <body> <div id="box"> <div id="top"> <p>Lightweight Calculator by PYJ</p> <input id="screen0" type="text" style="margin-top: 100px;" disabled/> <input id="screen1" type="text" style="margin-top: 190px;" disabled/> </div> <div id="main"> <span class="div1" style="font-size: 26px">(</span> <span class="div1" style="font-size: 26px">)</span> <span class="div1" style="font-size: 26px">DEL</span> <span class="div1">/</span> <span class="div2">7</span> <span class="div2">8</span> <span class="div2">9</span> <span class="div1">*</span> <span class="div2">4</span> <span class="div2">5</span> <span class="div2">6</span> <span class="div1">-</span> <span class="div2">1</span> <span class="div2">2</span> <span class="div2">3</span> <span class="div1">+</span> <span class="div2">0</span> <span class="div2">.</span> <span class="div1">C</span> <span class="div3" style="width: 94px;background: orangered;">=</span> </div> </div> </body> </html>
CSS/表现:
*{ margin: 0; padding: 0; font-family: "Consolas", "Menlo", "Courier", monospace; } span{display: block;} body{background: #f0f2f1;} #box{ width: 380px; height: 675px; margin-top: 8px; margin-left: auto; margin-right: auto; box-shadow:0 0 10px #888; } #top{ width: inherit; height: 285px; background: #333333; position: relative; } #top p{ color: #e7e7e7; font-size: 16px; padding-top: 5px; padding-right: 10px; text-align: right; } #top input{ width: 368px; height: 70px; position: absolute; color: #e8e8e8; background: none; border: none; font-size: 35px; text-align: right; line-height: 70px; cursor: text; padding-right: 10px; } #main{ width: inherit; height: 390px; background: #f0f2f1; } #main span{ width: 94px; height: 77px; border-right: 1px solid #dff0d8; border-top: 1px solid #dff0d8; float: left; font-size: 32px; text-align: center; line-height: 80px; cursor: pointer; } .div1{color: orangered;} .div2{color: #000;} .div3{border-right: none;color: #fff;} #main span:hover{background: #e1e1e1;} #main span:active{box-shadow: 0 0 5px 5px #fff;}
二、JavaScript/行为
行为层要解决的问题大致可以总结为:获取被点击的按钮内容,给出相应反应或组成表达式;计算表达式并显示。
1. 初始化(清屏):
1 window.onload = function(){ 2 var screen0 = document.getElementById('screen0'), //获取上显示器内容 3 screen1 = document.getElementById('screen1'); //获取下显示器内容 4 screen0.value = null; 5 screen1.value = null; 6 calculate(screen0, screen1); 7 };
2. 上面代码最后一行调用了calculate()函数,获取按钮字符:
1 function calculate(screen0,screen1){ 2 var box = document.getElementById('main'), //获取按钮盒子 3 count = 0; //记录显示器字符或数字个数 4 5 box.onclick = function(e){ 6 var symbol = e.target.innerText; //获取按钮字符 7 8 //内容是否超过允许长度 9 if((screen1.value + symbol).length > 40){ 10 alert('Content exceeds the maximum length!'); 11 return null; 12 } 13 if(symbol == 'C' ){ //清零 14 count = 0; 15 screen0.value = null; 16 screen1.value = null; 17 }else if(symbol != '='){ //表达式 18 if(count == -1){ //对上一次计算的清空 19 screen0.value += "=" + screen1.value; 20 screen1.value = symbol; 21 count = 1; 22 }else if(symbol == 'DEL'){ 23 if(screen1.value == null){ 24 count = 0; 25 }else{ 26 screen1.value = screen1.value.slice(0,-1); 27 count--; 28 } 29 }else{ 30 screen1.value += symbol; 31 } 32 }else if(symbol == '='){ //计算结果 33 screen0.value = screen1.value; 34 screen1.value = test(screen1.value ); 35 count = -1; 36 } 37 } 38 }
分别对清零、回删、“=”和其他字符情况做出判断,给予不同的处理结果。
3. 按了“=”,就表示要对前面的字符串进行计算了:
但在计算前,需要了解一下JS数字精度丢失的问题。计算机的二进制的实现和位数限制会使部分数无法有限表示,有穷的数在计算机的二进制里却是无穷的,因存储位数的限制而发生的“舍去”,就导致了精度的丢失。具体内容不再展开,可以去查看相关文档。
为了解决浮点数的精度丢失问题,在这里为Math对象定义了一个方法:
1 Math.formatFloat = function (exp, digit){ 2 var m = Math.pow(10, digit); 3 return parseInt(exp*m, 10)/m; 4 }; 5 //把小数放到位整数(乘倍数),再缩小回原来倍数(除倍数)
计算器最核心的算法就是表达式的运算。
本文的思想是,将表达式按符号拆分并运用递归:
1 function test(text) { 2 var index = 0; //记录符号索引 3 4 while(text){ 5 //首先计算括号内内容 6 if(text.lastIndexOf("(") > -1){ 7 index = text.lastIndexOf("("); 8 var endIndex = text.indexOf(")", index); 9 if(endIndex > -1) { 10 var result = Math.formatFloat(test(text.substring(index + 1, endIndex)) ,2); 11 return Math.formatFloat(test(text.substring(0, index) + result + text.substring(endIndex + 1)) ,2); 12 } 13 14 }else if(text.indexOf("+") >-1){ 15 index = text.indexOf("+"); 16 return Math.formatFloat(test(text.substring(0, index)) + test(text.substring(index + 1)) ,2); 17 18 }else if(text.lastIndexOf("-") > -1){ 19 index = text.lastIndexOf("-"); 20 if(text[index-1] == '*'){ 21 return Math.formatFloat(test(text.substring(0, index-1)) * test(text.substring(index)) ,2); 22 }else if(text[index-1] == '/'){ 23 return Math.formatFloat(test(text.substring(0, index-1)) / test(text.substring(index)) ,2); 24 }else{ 25 return Math.formatFloat(test(text.substring(0, index)) - test(text.substring(index + 1)) ,2); 26 } 27 28 }else if(text.lastIndexOf("*") > -1){ 29 index = text.lastIndexOf("*"); 30 return Math.formatFloat(test(text.substring(0, index)) * test(text.substring(index + 1)) ,2); 31 32 }else if(text.lastIndexOf("/") > -1){ 33 index = text.lastIndexOf("/"); 34 return Math.formatFloat(test(text.substring(0, index)) / test(text.substring(index + 1)) ,2); 35 36 }else{ 37 return Math.formatFloat(text,2); 38 } 39 } 40 41 return null; 42 }
需要注意:首先对括号内内容进行运算;“-” 号和除号要从表达式尾部开始提取,否则计算有误;“-” 号有负号和减号之分,解决办法是,在 “-” 号前有乘号和除号时,要按乘号或除号拆分;Math.formatFloat() 方法的运用。
使用效果图如下,存在bug或者文中有错误的话希望指出 [抱拳]