前端 JavaScript 實現一個簡易計算器

程式設計三昧發表於2021-07-09

前端使用 JavaScript 實現一個簡易計算器,沒有難度,但是裡面有些小知識還是需要注意的,算是一次基礎知識回顧吧。

題目

計算器

實現一個簡易版的計算器,需求如下:

1、除法操作時,如果被除數為0,則結果為0
2、結果如果為小數,最多保留小數點後兩位,如 2 / 3 = 0.67(顯示0.67), 1 / 2 = 0.5(顯示0.5)
3、請閱讀並根據提示補充完成函式initEvent、result和calculate
4、請不要手動修改html和css
5、不要使用第三方外掛

實現

HTML 檔案

<div class="calculator">
    <header class="cal-header">簡易計算器</header>
    <main class="cal-main">
        <div class="origin-value">0</div>
        <div class="cal-keyboard">
            <ul class="cal-items">
                <li data-action="num">7</li>
                <li data-action="num">8</li>
                <li data-action="num">9</li>
                <li data-action="operator">÷</li>
                <li data-action="num">4</li>
                <li data-action="num">5</li>
                <li data-action="num">6</li>
                <li data-action="operator">x</li>
                <li data-action="num">1</li>
                <li data-action="num">2</li>
                <li data-action="num">3</li>
                <li data-action="operator">-</li>
                <li data-action="num">0</li>
                <li data-action="operator">清空</li>
                <li data-action="operator">=</li>
                <li data-action="operator">+</li>
            </ul>
        </div>
    </main>
</div>

CSS 檔案

body, ul, li,select {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}
ul,li {list-style: none;}
.calculator {
    max-width: 300px;
    margin: 20px auto;
    border: 1px solid #eee;
    border-radius: 3px;
}
.cal-header {
    font-size: 16px;
    color: #333;
    font-weight: bold;
    height: 48px;
    line-height: 48px;
    border-bottom: 1px solid #eee;
    text-align: center;
}
.cal-main {
    font-size: 14px;
}
.cal-main .origin-value {
    padding: 15px;
    height: 40px;
    line-height: 40px;
    font-size: 20px;
    text-align: right;
    overflow: hidden;
    text-overflow:ellipsis;
    white-space: nowrap;
}
.cal-main .origin-type,
.cal-main .target-type {
    padding-left: 5px;
    width: 70px;
    font-size: 14px;
    height: 30px;
    border: 1px solid #eee;
    background-color: #fff;
    vertical-align: middle;
    margin-right: 10px;
    border-radius: 3px;
}
.cal-keyboard {
    overflow: hidden;
}
.cal-items {
    overflow: hidden;
}
.cal-items li {
    user-select: none;
    float: left;
    display: inline-block;
    width: 75px;
    height: 75px;
    text-align: center;
    line-height: 75px;
    border-top: 1px solid #eee;
    border-left: 1px solid #eee;
    box-sizing: border-box;
}
li:nth-of-type(4n+1) {
    border-left: none;
}
li[data-action=operator] {
    background: #f5923e;
    color: #fff;
}
.buttons {
    float: left;
    width: 75px;
}
.buttons .btn {
    width: 75px;
    background-color: #fff;
    border-top: 1px solid #eee;
    border-left: 1px solid #eee;
    height: 150px;
    line-height: 150px;
    text-align: center;
}
.btn-esc {
    color: #ff5a34;
}
.btn-backspace {
    position: relative;
}

JavaScript 檔案

var Calculator = {
    init: function () {
        var that = this;
        if (!that.isInited) {
            that.isInited = true;
            // 儲存操作資訊
            // total: Number, 總的結果
            // next: String, 下一個和 total 進行運算的資料
            // action: String, 運算子號
            that.data = {total: 0, next: '', action: ''};
            that.bindEvent();
        }
    },
    bindEvent: function () {
        var that = this;
        // 獲取 .cal-keyboard 元素
        var keyboardEl = document.querySelector(".cal-keyboard");
        keyboardEl && keyboardEl.addEventListener('click', function (event) {
            // 獲取當前點選的dom元素
            var target = event.target;
            // 獲取target的 data-action 值
            var action = target.dataset.action;
            // 獲取target的內容
            var value = target.innerText;
            if (action === 'num' || action === 'operator') {
                that.result(value, action === 'num');
            }
        });
    },
    result: function (action, isNum) {
        var that = this;
        var data = that.data;
        if (isNum) {
            data.next = data.next === '0' ? action : (data.next + action);
            !data.action && (data.total = 0);
        } else if (action === '清空') {
            // 設定清空時的對應狀態
            data.total = 0;
            data.next = "";
            data.action = "";
        } else if (action === '=') {
            if (data.next || data.action) {
                data.total = that.calculate(data.total, data.next, data.action);
                data.next = '';
                data.action = '';
            }
        } else if (!data.next) {
            data.action = action;
        } else if (data.action) {
            data.total = that.calculate(data.total, data.next, data.action);
            data.next = '';
            data.action = action;
        } else {
            data.total = +data.next || 0;
            data.next = '';
            data.action = action;
        }

        // 獲取 .origin-value 元素
        var valEl = document.querySelector(".origin-value");
        // print(data)
        valEl && (valEl.innerHTML = data.next || data.total || '0');
    },
    calculate: function (n1, n2, operator) {
        n1 = +n1 || 0;
        n2 = +n2 || 0;
        if (operator === '÷') {
            // 獲取除法的結果
            return n2 === 0 ? 0 : Math.floor((n1 / n2) * 100) / 100;
        } else if (operator === 'x') {
            // 獲取乘法的結果
            return Math.floor((n1 * n2) * 100) / 100;
        } else if (operator === '+') {
            // 獲取加法的結果
            return Math.floor((n1 + n2) * 100) / 100;
        } else if (operator === '-') {
            // 獲取減法的結果
            return Math.floor((n1 - n2) * 100) / 100;
        }
    }
};
Calculator.init();

分析

主要分析一下本題中 JavaScript 部分涉及的知識點。

事件委託

我們沒有給數字和符號元素分別新增點選事件,而是透過給它們的父元素 .cal-keyboard 新增點選事件,再透過事件冒泡獲得它父元素繫結的事件響應,使用事件委託有以下優點:

  • 減少記憶體消耗
  • 可以方便地動態新增或者刪除元素
  • 管理的函式減少
  • 減少操作 DOM 節點的次數,提高效能

事件委託的步驟:

  1. 給父元素繫結響應事件
  2. 監聽子元素的冒泡事件
  3. 獲取觸發事件的目標元素

保留小數位

大家的第一反應可能是使用 toFixed() 方法,但是這個方法在小數位不足的情況下會在後面補 0,比如:

const num = 0.8;
num.toFixed(2) // 0.80

可以看到,這個是不符合要求的。

還有一個問題需要注意:小數的相加結果可能並不符合預期,比如:

console.log(0.2 + 0.4) // 0.6000000000000001

這裡我們建議使用 Math.floor() 方法來處理小數位,先給結果乘以 100,再透過 Math.floor() 取得整數部分,然後除以 100,得到符合要求的結果,比如:

const num1 = 0.5;
const num2 = 0.68751;
Math.floor(num1 * 100)/100       // 0.5
Math.floor(num2 * 100)/100        // 0.68

~

~ 本文完

~

學習有趣的知識,結識有趣的朋友,塑造有趣的靈魂!

大家好,我是〖程式設計三昧〗的作者 隱逸王,我的公眾號是『程式設計三昧』,歡迎關注,希望大家多多指教!

你來,懷揣期望,我有墨香相迎! 你歸,無論得失,唯以餘韻相贈!

知識與技能並重,內力和外功兼修,理論和實踐兩手都要抓、兩手都要硬!

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章