post-image

Xây dựng ứng dụng máy tính đơn giản bằng JavaScript

Tổng quan

Bài viết này sẽ hướng dẫn chi tiết cách xây dựng một ứng dụng máy tính đơn giản bằng JavaScript. Đến cuối bài viết, bạn sẽ xây dựng được một máy tính có chức năng gần giống như máy tính trên iPhone.

Đầu tiên, bạn phải xây dựng máy tính bằng HTML và CSS.

Máy tính bao gồm hai phần: display (màn hình) và keys (các phím).

<div class="calculator">
    <div class="calculator__display">0</div>
    <div class="calculator__keys">
        <button class="key--operator" data-action="add">+</button>
        <button class="key--operator" data-action="subtract">-</button>
        <button class="key--operator" data-action="multiply">×</button>
        <button class="key--operator" data-action="divide">÷</button>
        <button>7</button>
        <button>8</button>
        <button>9</button>
        <button class="key--equal" data-action="calculate">=</button>
        <button>5</button>
        <button>6</button>
        <button>4</button>
        <button>1</button>
        <button>2</button>
        <button>3</button>
        <button>0</button>
        <button data-action="decimal">.</button>
        <button data-action="clear">AC</button>
    </div>
</div>

Nhận sự kiện ấn phím từ người dùng.

Các thao tác cơ bản của người dùng khi sử dụng calculator:

  1. Một phím số (0 ~ 9) (number key)
  2. Một phím toán tử (+, -, ×, ÷) (operator key)
  3. Phím thập phân (decimal key)
  4. Phím bằng (equals key)
  5. Phím khởi tạo (clear key)

Các bước đầu tiên để xây dựng máy tính này là có thể (1) nhận sự kiện tất cả các phím bấm và (2) xác định loại phím được nhấn. Trong trường hợp này, chúng ta có thể sử dụng event delegation pattern trong JavaScript, vì các khóa đều là con của .calculator__keys.

const calculator = document.querySelector('.calculator');
const keys = calculator.querySelector('.calculator__keys');

keys.addEventListener('click', e => {
    if (e.target.matches('button')) {
        // Do something
    }
})

Tiếp theo, chúng ta có thể sử dụng thuộc tính data-action để xác định loại khóa được nhấn.

const key = e.target;
const action = key.dataset.action;

Nếu khóa không có thuộc tính data-action, nó phải là khóa số.

if (!action) {
    console.log('number key!');
}

Nếu khóa có data-action là add, subtract, multiple hoặc divide, chúng ta biết khóa là toán tử.

if (
    action === 'add' ||
    action === 'subtract' ||
    action === 'multiply' ||
    action === 'divide'
) {
    console.log('operator key!')
}

Nếu data-action là decimal, chúng ta biết người dùng đã nhấp vào decimal key. Tương tự cho các khóa equal và clear.

if (action === 'decimal') {
    console.log('decimal key!')
}

if (action === 'clear') {
    console.log('clear key!')
}

if (action === 'calculate') {
    console.log('equal key!')
}

Khi người dùng nhấn number key

Tại thời điểm này, nếu máy tính hiển thị 0 (số mặc định), khi người dùng ấn phím từ không đến 9 thì số 0 sẽ bị thay thế.

Nếu máy tính hiển thị số khác 0, số được nhấn sẽ được thêm vào số hiển thị.

Ta phải xác định được 2 thứ:

  1. Số lượng phím được nhấp
  2. Số hiển thị hiện tại

Ta có thể nhận được hai giá trị này thông qua thuộc tính textContent của khóa được:

const display = document.querySelector('.calculator__display')

keys.addEventListener('click', e => {
    if (e.target.matches('button')) {
        const key = e.target
        const action = key.dataset.action
        const keyContent = key.textContent
        const displayedNum = display.textContent
        // ...
    }
})

Nếu máy tính hiển thị 0, thì thay thế màn hình máy tính bằng phím bấm. Ta có thể làm vậy bằng cách dùng thuộc tính textContent

if (!action) {
    if (displayedNum === '0') {
        display.textContent = keyContent
    }
}

Nếu máy tính hiển thị số khác không, thì nối thêm phím đã nhấn vào số được hiển thị.

if (!action) {
    if (displayedNum === '0') {
        display.textContent = keyContent
    } else {
        display.textContent = displayedNum + keyContent
    }
}

Khi người dùng nhấn decimal key

Khi người dùng nhấn decimal key, một số thập phân sẽ được thêm vào màn hình. Nếu người dùng nhấn bất kỳ số nào sau khi nhấn decimal key, số đó cũng sẽ được thêm vào màn hình.

if (action === 'decimal') {
    display.textContent = displayedNum + '.'
}

Khi người dùng nhấn operator key

Ta thêm class ‘is-depressed’ để nhận biết người dùng đang nhấn operator key

if (
    action === 'add' ||
    action === 'subtract' ||
    action === 'multiply' ||
    action === 'divide'
) {
    key.classList.add('is-depressed')
}

Khi người dùng nhấn phím number key sau phím operator key

Màn hình trước đó sẽ được thay thế bằng số mới. operator key cũng phải được xóa bỏ class ‘is-depressed’.

keys.addEventListener('click', e => {
    if (e.target.matches('button')) {
        const key = e.target
        // ...

        // Remove .is-depressed class from all keys
        Array.from(key.parentNode.children)
            .forEach(k => k.classList.remove('is-depressed'));
    }
})

Tiếp theo, ta muốn cập nhật màn hình bằng phím được nhấn. Trước khi thực hiện việc này, ta cần phải biết liệu khóa trước có phải là operator key hay không bằng cách sử dụng thuộc tính data-previous-key-type.

const calculator = document.querySelector('.calculator')
// ...

keys.addEventListener('click', e => {
    if (e.target.matches('button')) {
        // ...

        if (
            action === 'add' ||
            action === 'subtract' ||
            action === 'multiply' ||
            action === 'divide'
        ) {
            key.classList.add('is-depressed')
            // Add custom attribute
            calculator.dataset.previousKeyType = 'operator'
        }
    }
})

Nếu previousKeyType là một operator, ta sẽ thay thế màn hình hiển thị bằng số đã nhấn.

if (displayedNum === '0' || calculator.dataset.previousKeyType === 'operator') {
    display.textContent = keyContent;
    calculator.dataset.previousKeyType= "";
} else {
    display.textContent = displayedNum + keyContent;
    calculator.dataset.previousKeyType= "";
}

Khi người dùng nhấn equals key, máy tính sẽ tính kết quả phụ thuộc vào ba giá trị:

  1. Số đầu tiên được nhập vào
  2. Operator key
  3. Số thứ hai được nhập

Sau khi tính toán, kết quả sẽ thay thế ở màn hình hiển thị.

Để có được số đầu tiên,  ta cần lưu trữ giá trị hiển thị của máy tính trước khi xóa.

Để có được toán tử, chúng ta cũng có thể sử dụng phương pháp tương tự.

if (
    action === 'add' ||
    action === 'subtract' ||
    action === 'multiply' ||
    action === 'divide'
) {
    // ...
    calculator.dataset.firstValue = displayedNum;
    calculator.dataset.operator = action;
}

Sau đó bắt đầu thực hiện phép tính.

if (action === 'calculate') {
    const firstValue = calculator.dataset.firstValue
    const operator = calculator.dataset.operator
    const secondValue = displayedNum

    display.textContent = calculate(firstValue, operator, secondValue)
}

Chúng ta có function calculate:

function calculate(firstNum,operator,secondNum){
    if(operator === 'add'){
        return parseFloat(firstNum) +parseFloat(secondNum) ;
    }
    if(operator === 'subtract'){
        return parseFloat(firstNum) -parseFloat(secondNum) ;
    }
    if(operator === 'multiply'){
        return parseFloat(firstNum) *parseFloat(secondNum) ;
    }
    if(operator === 'divide'){
        return parseFloat(firstNum) /parseFloat(secondNum) ;
    }
}

Ta phải chỉnh sửa decimal key. Chỉ thêm 1 decimal key vào số hiển thị.

if (action === 'decimal') {
    console.log('decimal key!');
    if  (!displayedNum.includes('.')){
        display.textContent = displayedNum + '.';
    }

}

Tiếp theo, nếu người dùng nhấn decimal key sau khi nhấn operator key, màn hình sẽ hiển thị 0.

Gán giá trị cho previousKey.

if (!action) {
    // ...
    calculator.dataset.previousKeyType = 'number'
}

if (action === 'decimal') {
    // ...
    if (!displayedNum.includes('.') &amp;&amp; calculator.dataset.previousKeyType != ‘operator’) {
        display.textContent = displayedNum + '.'
    } else if (previousKeyType === 'operator') {
        display.textContent = '0.'
    }
    
    calculator.dataset.previousKeyType = 'decimal'
	
}

if (action === 'clear') {
    // ...
    calculator.dataset.previousKeyType = 'clear'
}

if (action === 'calculate') {
    // ...
    calculator.dataset.previousKeyType = 'calculate'
}

Chúng ta phải chỉnh sửa calculate key để hoàn thiện ứng dụng này. Chỉ tính toán khi giá trị firstValue và operator khác undefine:

if (action === 'calculate') {
    console.log('equal key!');
    const firstValue = calculator.dataset.firstValue;
    const operator = calculator.dataset.operator;
    const secondValue = displayedNum;
    
    if(firstValue!= undefined &amp;&amp; operator !=undefined){
        display.textContent = calculate(firstValue, operator, secondValue);
        calculator.dataset.previousKeyType = 'calculate';
    }
}

Tới đây, chúng ta đã hoàn thành xong ứng dụng máy tính đơn giản bằng JavaScript.

Bạn có thể download file Full-Code tại đây.

Vì ứng dụng còn nhiều chức năng chưa được làm, bạn hãy tự phát triển và xây dựng chúng theo cách của bạn mong muốn.

Chúc các bạn thành công!

Author: Nguyễn Đình Phú

Các bạn có thể tham khảo các bài viết hay về JavaScript tại đây.


Hãy tham gia nhóm Học lập trình để thảo luận thêm về các vấn đề cùng quan tâm.

Trở thành lập trình viên từ con số 0
Tags:
,

Leave a Reply

Your email address will not be published. Required fields are marked *