想嘗試做一個網頁版計算器后,參考了很多博客大神的代碼,綜合歸納、總結修改,整理如下文。
一、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或者文中有錯誤的話希望指出 [抱拳]

