![](https://habrastorage.org/webt/eh/sf/c3/ehsfc3qwva2or9wdpadgviptcde.gif)
Доброго времени суток, друзья!
В этой статье мы с вами, как следует из названия, напишем простой калькулятор на JavaScript.
Желание написать калькулятор возникло у меня после просмотра одного туториала, посвященного созданию «simple calculator», который оказался далеко не симпл и толком ничего не умел делать.
Наш калькулятор будет true simple (42 строки кода, включая пробелы между блоками), но при этом полнофункциональным и масштабируемым.
Для расчетов будет использоваться эта замечательная библиотека (Math.js).
Без дальнейших предисловий, приступаем к делу.
Наша разметка выглядит так:
<!-- head -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/6.6.4/math.js"></script>
<!-- body -->
<div class="calculator">
<output></output>
</div>
Здесь мы подключаем библиотеку, создаем контейнер для калькулятора и поле для вводимых символов и результата.
Подключаем стили:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
height: 100vh;
background: radial-gradient(circle, skyblue, steelblue);
display: flex;
justify-content: center;
align-items: center;
}
.calculator {
width: 320px;
height: 480px;
background: #eee;
border-radius: 5px;
box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.2), -2px -2px 3px rgba(0, 0, 0, 0.2);
}
output {
display: flex;
justify-content: center;
align-items: center;
width: 300px;
height: 40px;
background: #fff;
margin: 10px auto;
border-radius: 5px;
font-size: 1.4em;
font-weight: bold;
box-shadow: inset 1px 1px 1px rgba(0, 0, 0, 0.3),
inset -1px -1px 1px rgba(0, 0, 0, 0.3);
}
.keyboard {
height: 440px;
display: flex;
flex-wrap: wrap;
justify-content: space-evenly;
align-items: flex-start;
}
button {
margin: 0.5em 1em;
width: 40px;
height: 40px;
display: flex;
justify-content: center;
align-items: center;
background: none;
border: none;
cursor: pointer;
font-size: 1em;
font-weight: bold;
}
Вот что мы имеем на данный момент:
![](https://habrastorage.org/webt/vn/-m/xd/vn-mxdn4xozdljwn1iqr9gicgpu.png)
Кнопки будут генерироваться программно.
Переходим к скрипту.
Определяем поле для вывода результата и создаем контейнер для клавиатуры:
const output = document.querySelector('output')
const div = document.createElement('div')
div.classList.add('keyboard')
document.querySelector('.calculator').appendChild(div)
Наша строка с символами выглядит так:
'C CE % / 7 8 9 * 4 5 6 - 1 2 3 + 0 ( ) ='
Преобразуем данную строку в массив и создаем кнопки:
// разделителем служит пустая строка
// можно было бы обойтись без пробелов, если бы у нас не было "CE"
'C CE % / 7 8 9 * 4 5 6 - 1 2 3 + 0 ( ) ='.split(' ')
// пробегаемся по массиву
// для каждого символа
// создаем кнопку с помощью строкового литерала
// записываем значение символа в атрибут "value" кнопки
.map(symbol => {
div.insertAdjacentHTML('beforeend', `<button value="${symbol}">${symbol}</button>`)
})
Находим созданные кнопки и добавляем к ним обработчик события «клик»:
document.querySelectorAll('button').forEach(button => {
button.addEventListener('click', function () {
// по клику вызывается функция со значением кнопки в качестве параметра
calc(this.value)
})
})
Мы также хотим иметь возможность вводить символы с помощью клавиатуры. Для этого нам необходимо добавить обработчик события «нажатие клавиши» к объекту «Document» или «Window», затем отфильтровать ненужные значения свойства «ключ» клавиши, например, с помощью регулярного выражения:
document.addEventListener('keydown', event => {
if ((event.key).match(/[0-9%\/*\-+\(\)=]|Backspace|Enter/)) calc(event.key)
})
Метод «match» в данном случае играет роль фильтра: он не позволяет передавать функции «calc» аргумент, не соответствующий заданному в нем условию.
Само условие звучит так: если значением event.key является один из символов, указанных в квадратных скобках ([]; цифра от 0 до 9, знаки деления, умножения, сложения, вычитания, открывающая, закрывающая круглые скобки или знак равенства; обратная косая черта — экранирование) или (| — альтерация) Backspace, или Enter, то вызываем calc с event.key в качестве параметра, иначе ничего не делаем (Shift также успешно отбрасывается).
Наша главная (и единственная) функция «calc» выглядит следующим образом (код следует читать снизу вверх):
// функция принимает значение кнопки или ключ клавиши
function calc(value) {
// если нажат знак равенства или Enter
if (value.match(/=|Enter/)) {
// пробуем выполнить операцию
try {
// вычисляем значение строки
// это возможно благодаря методу "evaluate" объекта "math"
// Math.trunc используется для округления до целого числа
output.textContent = Math.trunc(math.evaluate(output.textContent))
// если операцию выполнить невозможно
} catch {
// сохраняем значение поля
let oldValue = output.textContent
// создаем новую переменную
let newValue = 'недопустимое выражение'
// выводим значение новой переменной в поле
output.textContent = newValue
// через полторы секунды возвращаем полю старое значение
setTimeout(() => {
output.textContent = oldValue
}, 1500)
}
// если нажат символ "C"
} else if (value === 'C') {
// очищаем поле
output.textContent = ''
// если нажат символ "СЕ" или Backspace
} else if (value.match(/CE|Backspace/)) {
// уменьшаем строку на один символ
output.textContent = output.textContent.substring(0, output.textContent.length - 1)
// если нажата любая другая (отфильтрованная) кнопка или клавиша
} else {
// записываем ее значение в поле
output.textContent += value
}
}
В завершение, парочка слов о заявленной масштабируемости и полнофункциональности.
Метод «evaluate» (ранее «eval») и другие методы Math.js имеют очень большие возможности. Опираясь на эти возможности, мы можем легко расширить функционал нашего калькулятора, добавив в него новые символы и операторы, предусмотрев возможность работы с числами с плавающей точкой (регулируя количество знаков после запятой с помощью переключателя и метода «toFixed») и т.д.
Результат:
Код на GitHub.
Благодарю за внимание. Надеюсь, вы нашли для себя что-то полезное. Хороших выходных и счастливого кодинга.