В первой части «Простой калькулятор при помощи HTML/CSS/JS» я рассказал как я создавал репозиторий на github и как необходимо оформить html и css. Во-второй части я расскажу как необходимо оформить файл .js, запустить калькулятор и опубликовать его на github.

Для наглядного понимания, что за чем идет я составил не большой план. В некоторых случаях пункты плана будут повторяться так, как будем обновлять и дополнять функции.

План следующий:

  1. Получить все найденные элементы документа (querySelectorAll)
  2. Создать класс для хранения данных
  3. Создать объект new Calculator
  4. Вывод значений на экран калькулятора — функция
  5. Получить значение при клике на кнопку + функция (операнды)
  6. Выбор оператора + функция (оператор)
  7. Подсчет и вывод результата на экран калькулятора
  8. Функция стереть все AC — стирает все введенные значения
  9. Функция DEL стирает последнее значение
  10. Разделить запятыми тыс., млн. и т.д.
  11. Обновить отображение на экране калькулятора

Для того чтобы мы знали какая кнопка была нажата пользователем нам необходимо добавить класс каждой кнопки в html файле, но место классов добавим data -атрибут: data-operation — для всех операций, data-number — для всех чисел, data-equals — для знака «=», data-delete — для кнопки «DEL», data-all-clear — для кнопки «AC», data-previous-operand — первый операнд «экран ввода», data-current-operand — второй операнд «экран ввода». Используя data -атрибут место классов, можно сразу сказать какая часть используется scc, а какая JS.

const numberButtons = document.querySelectorAll('[data-number]');
const operationButtons = document.querySelectorAll('[data-operation]');
const equalsButton = document.querySelector('[data-equals]');
const deleteButton = document.querySelector('[data-delete]');
const allClearButton = document.querySelector('[data-all-clear]');
const previousOperandTextElement = document.querySelector(
  '[data-previous-operand]'
);
const currentOperandTextElement = document.querySelector(
  '[data-current-operand]'
);

Чтобы получить данные из html воспользуемся самым универсальным методом поиска – это document.queySelectorAll(), он возвращает все элементы внутри document.

Следующим этапом необходима придумать как мы будем сохранять все наши данные которые были введены. Для этого создадим класс калькулятор и воспользуемся специальным методом, служащий для создания и инициализации объектов это — конструктор. В метод конструктор передаем два элемента (предыдущий операнд и текущий операнд) — это делается для того чтобы мы знали где необходимо разместить текст для нашего калькулятора. Для доступа к информации внутри объекта будем использовать ключевое слово this.

Калькулятор имеет следующие функции: удалить все значение, сложения, вычитания, умножение и т.д. Эти функции необходимо определить в созданном классе Calculator. Например функция clear — стирает все значения, delete — удаляет последнее значение, appendNumber добавляют значения в поле ввода калькулятора, а функция chooseOperation добавляет операцию которую необходимо провести над операндами. Функция compute непосредственно проводит операцию над выражением и выдает результат.

Функции калькулятора

Давайте определимся, что содержит внутри себя функция clear. Если введены какие либо операнды или операция их необходимо стереть. При вызове функции clear место операндов и операции будет пустая строка и значение null. А вызов функции this.clear() помещаем в наш конструктор.

На следующем шаге необходимо создать объект new Calculator с таким же именем как и конструктор.

Выберем значения с помощью numberButtons, перебираем кнопки методом foreach и к каждому значению применяем функцию. Метод addEventListener регистрирует определенный обработчик событий (когда мы нажимаем на кнопку мы хотим сделать что — то, а именно вывести на экран калькулятора значение). Метод innerText позволяет получить текстовое содержимое элемента, а с помощью updateDisplay выводим на экран калькулятора данные значения.

Настало время протестировать калькулятор, для этого необходимо добавить в функции appendNumber и updateDisplay следующие значения.

  clear() {
    this.currentOperand = '';
    this.previousOperand = '';
    this.operation = null;
  }

  delete() {}
  appendNumber(number) {
    this.currentOperand = number;
  }
  chooseOperation(operation) {}
  compute() {}
  updateDisplay() {
    this.currentOperandTextElement.innerText = this.currentOperand;
  }
 
}

И при нажатии кнопок на калькуляторе каждый раз значения обновляются и появляются на экране калькулятора.

На данный момент мы знаем, что калькулятор выводит данные на экран. Но для того чтобы выводилось целое число нам необходимо перевести текущий оператор в строку и соединить их с помощью знака «+». Перед этим необходимо поставить условие, что если будет введена «.» и текущая строка уже содержит точку, то добавлять ее не нужно.

Теперь необходимо добавить знак операции которую мы хотим провести над нашими операндами. Чтобы это сделать, скопируем данные из numberButtons и заменим их на:

operationButtons.forEach(button => {
  button.addEventListener('click', () => {
    calculator.chooseOperation(button.innerText);
    calculator.updateDisplay();
  });
});

Но это еще не все. В нашем калькуляторе необходимо сделать так, чтоб первый введенный операнд перемещался на верхнею строку, а текущий операнд занимал место предыдущего операнда. Для этого напишем следующий код и поместим его в наш конструктор Calculator. Так же укажем условие если не передана операцию и если операция уже существует.

 chooseOperation(operation) {
    if (this.currentOperand === '') {
      return;
    }
    if (this.previousOperand !== '') {
      return this.compute();
    }
    this.operation = operation;
    this.previousOperand = this.currentOperand;
    this.currentOperand = '';
  }

На данном этапе мы переходим к функции вычисления. Сперва нужно получить данные, запустить нашу функцию compute и вывести результат на экран нашего калькулятора. Должен получиться следующий код:

equalsButton.addEventListener('click', button => {
  calculator.compute();
  calculator.updateDisplay();
});

Вводим переменную, куда будем записывать наш результат. Так как все операнды у нас строки их необходимо перевести в числа. Для этого существует функция parseFloat которая принимает строку в качестве аргумента и возвращает десятичное число (число с плавающей точкой). необходимо поставить условие, если не введены операнды, то ничего не должно происходить. Затем с помощью конструкции switch выбираем какое действие необходимо провести над операндами. Я также добавил метод toFixed(5) который округляет число до 5 знаков после запятой.

Кнопка стереть все — делаем как в предыдущих примерах.

allClearButton.addEventListener('click', button => {
  calculator.clear();
  calculator.updateDisplay();
});

Функция delete отличается от функции allClear. Первая удаляет только последнее значение, а вторая стирает все. Чтобы правильно написать функцию delete достаточно будет просто получить текущее значение, перевести его в строку и с помощью метода slice оставить всю строку, кроме последнего символа.

delete() {    //Пишем в классе Calculator
    this.currentOperand = this.currentOperand.toString().slice(0, -1);
  }

// Эту часть как обычно
deleteButton.addEventListener('click', button => {
  calculator.delete();
  calculator.updateDisplay();
});

Функция getDisplayNumber поможет нам правильно отображать значение на экране калькулятора (запятые — разделитель тыс. и точка если не целое число). Данная функция принимает число, сразу переведем ее в строку это необходимо для дальнейших манипуляций. Введем переменные которые разделять целую часть числа от дробной. Метод toLocaleString возвращает строку с языко-зависимым представлением числа — устанавливаем значение «en». Благодаря этому методу тыс. сотни тыс. и т.д. будут разделыены запятыми.

 getDisplayNumber(number) {
    const stringNumber = number.toString();
    const integerDigits = parseFloat(stringNumber.split('.')[0]);
    const decimalDigits = stringNumber.split('.')[1];
    let integerDisplay;
    if (isNaN(integerDigits)) {
      integerDisplay = '';
    } else {
      integerDisplay = integerDigits.toLocaleString('en', {
        maximumFractionDigits: 0
      });
    }
    if (decimalDigits != null) {
      return `${integerDisplay}.${decimalDigits}`;
    } else {
      return integerDisplay;
    }
  }

И завершающий штрих данного калькулятора это небольшое, но важное обновление функции updateDisplay. Мы ранее ее писали, но сейчас нужно обновить. Везде где мы получали операнды, мы должны сперва отправить их в предыдущую функцию getDisplayNumber.

Команды для публикации проекта на github

Теперь с помощью команд git status — проверяем какой статус у наших фаилов (измененные фаилый или те которые не находятся в репозитории будут высветлены красным цветом), git add . — данная команда добавляет под версионный контроль все фаилы, git commit -m «comment» — записывает индексированные изменения в репозиторий и команда git push — публикует все изменения.

Весь код находится в открытом доступе в репозитории на github.