純JS計算器 實現鍵盤輸入、歷史回點
2018-10-9-2018-10-11
這幾天用原生JS學習寫計算器的過程中,遇到了如下幾個問題(按遇到的順序闡述):
1.靜態佈局問題
關於這點,如果以後還有佈局問題,首先應該考慮到div的排布堆疊問題,計算好每個div的各類引數。關於美感問題,不作考慮,現階段只關注功能實現問題。
2 JS設計
2.1 在計算器顯示屏的顯示環節中,缺乏了對JS語言型別的認識,未理解弱語言型別的意思,JS會將同類型的變數相加減 (拼接)。
後面考慮到這點,優化了程式碼。方法傳的是value屬性,在按的時候計算符號也可以同步渲染到顯示屏中,提高了使用者互動性。
2.2
涉及四則運算時,不知道如何將其通過一個“=”按鍵的點選事件得出一個新的num.value回傳,如下圖,瞎搞了幾天只能得到NaN。
10-11中午看其他人寫的程式碼後,接觸到了JS自帶的eval()函式
eval() 函式可計算某個字串,並執行其中的的 JavaScript 程式碼。
1.該方法只接受原始字串作為引數,如果 string 引數不是原始字串,那麼該方法將不作任何改變地返回。因此不能為 eval() 函式傳遞 String 物件來作為引數。
2.如果試圖覆蓋 eval 屬性或把 eval() 方法賦予另一個屬性,並通過該屬性呼叫它,
則 ECMAScript 實現允許丟擲一個 EvalError 異常。
2.3 解決好顯示問題之後,比較輕鬆的解決了清零鍵問題。
2.4 測試時有時候會按錯數字,導致要全盤重輸,增加了一個回退鍵功能。
編寫JS方法的過程中,未能很好的把顯示屏數值理解通透(實際上就是一個字串,之前寫的那些程式碼也不算徹底分析清楚如何運作的)。波折之後程式碼如下:
substring(start,stop) 方法:
返回的子串包括 start 處的字元,但不包括 stop 處的字元。
提取字串中介於兩個指定下標之間的字元。
計劃考慮:
一、 增加一個鍵盤同步輸入計算的功能。 (10-13 21:12解決一部分)
1.1 通過事件中的鍵盤監聽 封裝了方法,成功實現了數字鍵輸入。
此時遇到了一個問題,當我以如下圖單獨設定了各個功能鍵的操作, 是由shift+8構成,鍵盤輸入時不能同時監聽到前後兩個按鍵,導致鍵入後顯示為8。
1.2優化了程式碼,採用switch()語句,提高了閱讀性。
1.2 組合鍵事件(10-14-9:30)
組合按鍵一般分以下兩種:
兩位組合建,如:ctrl(cmd)+ 其他按鍵,alt+其他按鍵,shift+其他按鍵
三位組合鍵,如:ctrl(cmd)+ shift + 其他按鍵,Ctrl(cmd)+ alt + 其他按鍵
在組合鍵中,js的event中有以下幾種屬性:ctrlKey(metaKey)、altKey、shiftKey
通過組合鍵事件。
實現了的監聽,但顯示屏同時輸入了8。
(9:33)經過驗證,組合事件返回值為布林值。故將上圖函式植入數值8的監聽中,處理後可以單獨監聽8和*的使用。
(9:50)完成了所有非數字鍵的設定。
1.3 優化了鍵盤輸入模式下的計算優先順序,()。
結果為8
結果為5
二、 增加移動端的顯示樣式(10-13解決)
三、 浮點運算丟失精度(小數),需提升精度。
四、 未考慮低版本IE及其他瀏覽器的相容效果。
五、 新增開方、冪次運算、增加計算曆史回撥功能(即還原當初得結果前的那一步)
5.1 控制檯獲得歷史計算串(利用陣列push方法)
5.2 新建個方法,將其投射到新建的ul中
5.2.1動態建立li來一一存放歷史計算穿
初步封裝一個函式,使得在耗費系統性能的情況下,實現了動態顯示的效果,如下圖所示。
由控制檯輸出和函式的設定,每次執行後都會多創造很多的Li。(17:02)
5.2.2 成功實現了精準的歷史回顯功能,不會造成效能的浪費。
定義了一個全域性變數,來控制Li的建立。(21:10)
全域性變數破壞了函式的封裝效能。函式中如果使用了全域性變數,那函式體內的語句就可以繞過函式引數和返回值進行存取,這破壞了函式的獨立性,使函式對全域性變數產生依賴,也降低了該函式的可移植性。
5.3 設計通過(回顯框)點選事件,使得可以之前的某步操作。(10/14 21:51)
通過jQuery的事件代理成功實現。
5.4 多次計算後,會產生過多的高度,需要完善回顯框的樣子。(10/15 9:00)
在這過程中,發現一個問題。如果不想要那條記錄,只能點選回顯,而不能刪除,影響了使用者體驗。
5.5 (10:00卒)回顯的值是字串型別(=功能報錯),想通過點選一個非字串標籤的想法失敗。
以下為原始碼分享:
<!DOCTYPE html><html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1" >
<title>計算器的實現</title>
</head>
<script src="lib/jquery/jquery.min.js"></script>
<script type="text/javascript">
//定義一個數組來儲存歷史字串
var lastNums = [];
//計算器按鍵封裝
function calCulate(val){
var num = document.getElementById("screen");
switch(val){
case "=":
/*
eval() 函式可計算某個字串,並執行其中的的 JavaScript 程式碼。
1. 該方法只接受原始字串作為引數,如果 string 引數不是原始字串,
那麼該方法將不作任何改變地返回。因此請不要為 eval() 函式傳遞 String 物件來作為引數。
2. 如果試圖覆蓋 eval 屬性或把 eval() 方法賦予另一個屬性,並通過該屬性呼叫它,
則 ECMAScript 實現允許丟擲一個 EvalError 異常。
*/
lastNums.push(num.value);
showHistroyNum();
num.value = eval(num.value);
break;
case "C":
num.value = "";
break;
default:
num.value = num.value + val;
break;
}
}
//substring(start,stop) 方法返回的子串包括 start 處的字元,但不包括 stop 處的字元。
//提取字串中介於兩個指定下標之間的字元。
delNum = function () {
//獲取ID
var screen = document.getElementById("screen");
var str = screen.value;
//擷取字串 (0至倒數第二位)並返回
str = str.substring(0, str.length - 1);
str = (str == "" ? "" : str);
screen.value = str;
};
//定義一個鍵盤監聽按鈕
$(document).keydown(function (event) {
var num = document.getElementById("screen");
var e = event || window.event;
var Key = event.keyCode;
switch (Key){
case 48 :
if(e.shiftKey){
val = ")" ;
calCulate(val);
break;
}
getVal(Key);
break;
case 49 :
getVal(Key);
break;
case 50 :
getVal(Key);
break;
case 51 :
getVal(Key);
break;
case 52 :
getVal(Key);
break;
case 53 :
getVal(Key);
break;
case 54 :
getVal(Key);
break;
case 55 :
getVal(Key);
break;
case 56 :
if(e.shiftKey){
val = "*" ;
calCulate(val);
break;
}
getVal(Key);
break;
case 57 :
if(e.shiftKe0y){
val = "(" ;
calCulate(val);
break;
}
getVal(Key);
break;
//功能鍵區
case 8 :
delNum();
break;
case 187 :
val = "+" ;
calCulate(val);
break;
case 189 :
val = "-" ;
calCulate(val);
break;
case 190 :
val = "." ;
calCulate(val);
break;
case 191 :
val = "/" ;
calCulate(val);
break;
case 13:
reCall();
num.value = eval(num.value);
}
// 歷史按鍵處理
/*
//數字按鍵區
if(Key == 48){
getVal(Key)
}
if(Key == 49){
getVal(Key)
}
if(Key == 50){
getVal(Key)
}
if(Key == 51){
getVal(Key)
}
if(Key == 52){
getVal(Key)
}
if(Key == 53){
getVal(Key)
}
if(Key == 54){
getVal(Key)
}
if(Key == 55){
getVal(Key)
}
if(Key == 56){
getVal(Key)
}
if(Key == 57){
getVal(Key)
}
//功能按鍵區
if(Key == 8){
delNum();
}
if(Key == 13){
val = "=" ;
calCulate(val);
}
if(Key == 107){
val = "+" ;
calCulate(val);
}
if(Key == 109){
val = "-" ;
calCulate(val);
}
if(Key == 110){
val = "." ;
calCulate(val);
}
if(Key == 111){
val = "/" ;
calCulate(val);
}
if(Key == 106){
val = "*" ;
calCulate(val);
}*/
});
function getVal(Key){
//
val = Key - 48;
//轉入計算器封裝函式
calCulate(val);
}
//定義一個歷史回顯功能
window.i = 0;
function showHistroyNum(){
//獲取回顯框
var lastShow = document.getElementById("lastShow");
var allLis = lastShow.children;
// 10.14 20:00 動態建立記錄的遍歷
if(lastNums.length >= allLis.length){
var li = document.createElement("li");
lastShow.appendChild(li);
allLis[i].innerText = lastNums[i];
i++;
}
}
// 10.14 18:00 動態建立記錄的遍歷
/*
for(var i = 0 ; i< lastNums.length; i++){
var li = document.createElement("li");
lastShow.appendChild(li);
allLis[i].innerText = lastNums[i];
}*/
//定義一個回顯框點選返回事件
$(function () {
var num = document.getElementById("screen");
$("#lastShow").delegate("li","click", function () {
num.value = this.innerText;
})
});
</script>
<style type="text/css">
body{
font-size:12px;
font-family:"微軟雅黑";
color:#666;
}
*{
padding:0px;
margin:0px;
}
#cac{
width:830px;
height:500px;
background:#f2f2f2;
padding:10px;
margin: 0 auto;
}
#cac .c_show #screen{
width:810px;
height:42px;
border:none;
line-height:42px;
text-align:right;
padding-right:20px;
font-size:34px;
color:#9e9e9e;
}
#cac h2{
font-size:16px;
color:#000;
font-weight:500;
padding:12px 0 12px 20px;
cursor:move;
text-align: center;
}
#cac .c_key {
border:3px solid #fff;
overflow:auto;
margin:10px auto;
}
#cac .c_key input[type=button]{
float:left;
width:140px;
height:65px;
background:#eaeaea;
margin:11px;
text-align:center;
line-height:65px;
font-size:32px;
cursor:pointer;
list-style:none;
border:1px solid #fff;
transition: all 200ms;
}
#cac .c_key input[type=button]:hover{
background:#fff;
color:#000;
}
#equ{
width: 37% !important;
}
#back{
width: 37% !important;
}
#lastShow{
width: 100%;
height: 100px;
overflow: scroll;
}
#lastShow li{
text-align: center;
font-size: 20px;
border-bottom:1px solid #cccccc;
cursor: pointer;
line-height: 20px;
height: 20px;
width: 100%;
}
/*小螢幕768px以下的自適應*/
@media screen and (max-width: 768px){
#cac{
width:280px;
height:350px;
background: #a18652;
padding:10px;
margin: 50px auto;
}
#cac .c_show #screen{
width:270px;
height:42px;
border:none;
line-height:42px;
text-align:right;
padding-right:10px;
font-size:34px;
color:#9e9e9e;
}
#cac .c_key{
border:3px solid #fff;
overflow:auto;
margin:10px auto;
}
#cac .c_key input[type=button]{
border-radius: 10px;
float:left;
width:52px;
height:35px;
background: #d4a840;
margin:8px;
text-align:center;
line-height:40px;
font-size:24px;
cursor:pointer;
list-style:none;
border:1px solid rgba(0,0,0,0.1);
}
#equ{
width: 44% !important;
}
#back{
width: 43% !important;
}
}
</style>
<body>
<div id="cac">
<h2>計算器(可鍵盤控制)</h2>
<div class="c_show">
<input type="text" name="t" id="screen" value="" >
</div >
<div class="c_key">
<input type="button" value="1" onclick="calCulate(this.value)">
<input type="button" value="2" onclick="calCulate(this.value)">
<input type="button" value="3" onclick="calCulate(this.value)">
<input type="button" value="4" onclick="calCulate(this.value)">
<input type="button" value="5" onclick="calCulate(this.value)">
<input type="button" value="6" onclick="calCulate(this.value)">
<input type="button" value="7" onclick="calCulate(this.value)">
<input type="button" value="8" onclick="calCulate(this.value)">
<input type="button" value="9" onclick="calCulate(this.value)">
<input type="button" value="0" onclick="calCulate(this.value)">
<input type="button" value="." onclick="calCulate(this.value)">
<input type="button" value="C" onclick="calCulate(this.value)">
<input type="button" value="+" onclick="calCulate(this.value)">
<input type="button" value="-" onclick="calCulate(this.value)">
<input type="button" value="*" onclick="calCulate(this.value)">
<input type="button" value="/" onclick="calCulate(this.value)">
<input type="button" value="←" id="back" onclick="delNum()">
<input type="button" id="equ" value="=" onclick="calCulate(this.value)">
</div>
</div>
<h2 style="text-align: center;">歷史回點區</h2>
<ul id="lastShow">
</ul>
</body>
</html>