ilyachalov (ilyachalov) wrote,
ilyachalov
ilyachalov

Categories:

JavaScript: задача про депозитный калькулятор

Решил задачу «Депозитный калькулятор» к подразделу 4.3 «События: change, input, cut, copy, paste» второй части учебника по JavaScript.

Текст постановки задачи гласит, цитата:

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


На самом деле, такая постановка неверно передает то, что нужно сделать. Интерфейс не нужно создавать, так как он уже создан на заданной тестовой HTML-странице, код которой можно посмотреть в песочнице.

Работа этого интерфейса в тексте постановки задачи не объясняется, нужно смотреть код тестовой HTML-страницы на языке HTML и анализировать работу демонстрационного примера, который можно посмотреть на странице задачи.

Интерфейс депозитного калькулятора на тестовой HTML-странице состоит из двух частей: 1) HTML-форма, в элементы которой пользователь может вводить данные (сумму первоначального депозита, срок вклада и годовую процентную ставку) и 2) HTML-таблица из трех строк и двух столбцов для визуализации введенных пользователем данных.

HTML-форма для ввода данных тоже содержит HTML-таблицу, с помощью которой элементы HTML-формы позиционируются на HTML-странице. Эта HTML-таблица тоже состоит из трех строк и двух столбцов. Элементов HTML-формы для ввода данных три: 1) HTML-элемент input с названием money и типом number для ввода суммы первоначального депозита; 2) HTML-элемент select с названием months и восемью опциями, одну из которых должен выбрать пользователь, для ввода срока вклада (3 месяца, 6 месяцев, 12 месяцев, 18 месяцев и так далее); 3) HTML-элемент input с названием interest и типом number для ввода годовой процентной ставки.

HTML-таблица для визуализации данных в первой строке содержит заголовки столбцов («Было:» и «Станет:»), во второй строке содержит две ячейки th с идентификаторами money-before и money-after (в которые от нас требуется вписать сумму первоначального депозита и итоговую сумму депозита с учетом срока вклада и годовой процентной ставки), в третьей строке содержит две ячейки td с красным и зеленым цветом фона.

Последние две ячейки представляют два столбца в этакой доморощенной диаграмме: красный столбец представляет сумму первоначального депозита, а зеленый столбец — итоговую сумму депозита. Отличие этих двух столбцов по высоте показывает пользователю визуально разницу между первоначальным депозитом и суммой, которую он получит по окончании срока вклада, включая набежавшие проценты. Высота красного столбца задана в 100 пикселей и ее изменение не предполагается. От нас требуется рассчитать и установить высоту зеленого столбца, для этого у этого столбца определен идентификатор height-after.

В тексте постановки задачи для расчета итоговой суммы депозита с учетом набежавших процентов дана формула на языке JavaScript, которую требуется использовать, чтобы программист не запутался:
// initial: начальная сумма денег
// interest: проценты, например, 0.05 означает 5% в год
// years: сколько лет ждать
let result = Math.round(initial * (1 + interest * years));

С одной стороны, эта формула должна быть известна любому школьнику, который изучил понятие процента, и можно было бы удивиться, зачем ее привели. С другой стороны, из этой формулы видно, что в данном случае предполагается, что набежавшие за год проценты не прибавляются к так называемому «телу депозита» (как говорят банковские работники), и процент в конце каждого расчетного периода считается от суммы первоначального депозита.

У меня в браузере этот интерфейс депозитного калькулятора выглядит так (картинка):



Тонкая серая линия слева и сверху — это граница области просмотра браузера. Я ее оставил на картинке, чтобы можно было понять, где относительно границы области просмотра расположено содержимое тестовой HTML-страницы.

* * *

Приступим к решению задачи. При загрузке тестовой HTML-страницы в HTML-форме депозитного калькулятора уже есть значения по умолчанию (они указаны в коде тестовой HTML-страницы). Диаграмма в начале эти значения пока не отображает (там не отображены сумма начального депозита и итоговая сумма депозита с набежавшими процентами, а также не отображен зеленый столбец, потому что его высота по умолчанию равна нулю).

Очевидно, что нам нужно создать функцию, которая будет переносить данные депозитного калькулятора в диаграмму. Почему это должна быть отдельная функция? Потому что ее код нужно будет сразу выполнить при загрузке тестовой HTML-страницы (для значений по умолчанию), а затем ее код потребуется выполнять каждый раз при изменении данных в элементах HTML-формы депозитного калькулятора. Понятно, что для такого использования код удобно оформить отдельной функцией. Пишем код:
let form = document.forms.calculator;

function dataVisualization() {
    //...
}

form.money.addEventListener("input", dataVisualization);
form.months.addEventListener("input", dataVisualization);   // можно и при событии change
form.interest.addEventListener("input", dataVisualization);

dataVisualization();

dataVisualization — это наша функция для переноса (визуализации) данных из HTML-формы депозитного калькулятора в диаграмму. Она запускается сразу же при открытии тестовой HTML-страницы в браузере (последняя строка кода) и вешается на событие input, которое «выстрелит» (по-английски «fired») при изменении значения конкретного элемента HTML-формы, то есть функцию вешаем в качестве функции-обработчика на указанное событие каждого из трех элементов HTML-формы депозитного калькулятора.

Если в HTML-элементах input для суммы первоначального взноса (с именем money) и для годовой процентной ставки (с именем interest) обязательно следует использовать событие input, то для HTML-элемента select для срока вклада (с именем months) можно использовать хоть событие input, хоть событие change (подробнее про это можно почитать в обсуждаемом подразделе учебника). Это следует из требования в тексте постановки задачи о том, что любое изменение данных должно обрабатываться (то есть отражаться в диаграмме) немедленно.

Как будет работать наша функция? Она должна получить данные из HTML-формы депозитного калькулятора, выполнить вычисление итоговой суммы депозита с набежавшими процентами по заданной формуле и отобразить результат в диаграмме. Дополним код:

let form = document.forms.calculator;

function dataVisualization() {
    // получим данные
    let initial = +form.money.value;          // преобразуем в число
    if (!initial) return;
    let interest = form.interest.value / 100; // преобразуем в число
    if (!interest) return;
    let years = form.months.value / 12;       // преобразуем в число
    if (!years) return;
    
    // произведем вычисление
    let result = Math.round(initial * (1 + interest * years));
    
    // отобразим результат
    window["money-before"].innerHTML = initial;
    window["money-after"].innerHTML = result;
    window["height-after"].style.height =
        Math.round(100 * (1 + interest * years)) + "px";
}

form.money.addEventListener("input", dataVisualization);
form.months.addEventListener("input", dataVisualization);   // можно и при событии change
form.interest.addEventListener("input", dataVisualization);

dataVisualization();

Это окончательный вариант скрипта, он у меня работает (и это решение очень похоже на решение авторов задачи). Но я хочу еще отметить несколько моментов, о которые спотыкался, пока писал этот код.

Несмотря на то, что HTML-элементы input в данном случае имеют тип number в коде на языке HTML, это вовсе не означает, что свойство form.money.value и свойство form.months.value должны возвратить число (а я так думал). На самом деле, они возвращают строку (подробнее).

В нашем случае это можно проигнорировать, так как при выполнении вычисления по указанным выше формулам эти значения попадают на операции умножения и деления, которые автоматически преобразовывают строковые операнды в числовые. Но на случай, если данный код будет как-то изменяться, я преобразовал form.money.value в число с помощью унарной операции + и пометил переменные, которые должны быть числами, комментарием «преобразуем в число». (В языках программирования с типизацией, кстати, это делается более изящно: просто указываешь тип переменной.)

Написать инструкции if (!initial) return; и две другие подобные я сам не догадался, дописал уже после того, как посмотрел решение от авторов задачи. По идее, логично прописать эти инструкции, так как каждый элемент из трех элементов HTML-формы депозитного калькулятора является обязательным для получения правильного результата (в коде тестовой HTML-страницы, кстати, это отмечено указанием атрибута required для этих HTML-элементов).

С идентификаторами money-before, money-after и height-after получилось забавно. Когда я пишу полностью сам свои скрипты и идентификаторы для скриптов, мне и в голову не приходит использовать в идентификаторах дефисы, потому что у меня уже в подкорке забито, что дефисы в идентификаторах использовать нельзя, но явно (сознательно) я этого не помню. Так что некоторое время тупил в ошибки скрипта. На самом деле, такие идентификаторы можно использовать, но не прямо, а, к примеру, указанным выше способом, через глобальный объект window. (Про то, что дефис не разрешен в идентификаторах, было рассказано в подразделе 2.4 «Переменные» первой части обсуждаемого учебника. Про то, как можно обратиться к HTML-элементу с идентификатором, содержащим дефис, было рассказано в подразделе 1.4 «Поиск: getElement*, querySelector*» второй части обсуждаемого учебника.)
Tags: Образование, Программирование
Subscribe

  • JavaScript: Blob

    Прочел подраздел 2.3 « Blob» третьей части учебника по JavaScript. Для меня эта статья оказалась настолько объемной в плане нового, что ее разбор…

  • Сказка про двоичные данные, кодировку Windows-1251 и Юникод

    Вопрос из комментариев к подразделу 2.3 « Blob» третьей части учебника по JavaScript, цитата: Не могу найти способ выдавать файл с кодировкой…

  • Как работает кодирование Base64, окончание

    Начало: « Как работает кодирование Base64». Пример второй. « Картинка-смайлик» В предыдущем примере кусок двоичных данных, содержащий текст,…

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments