1. 程式人生 > >純JS計算器 實現鍵盤輸入、歷史回點

純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>