Стать программистом. Практика JavaScript для новичков. Занятие 7.

в прошлом году

Доброго времени суток, друзья!

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

Однако, в нашей функциональности с кнопками все еще есть шероховатые места. Давайте их сгладим.

Стать программистом. Практика JavaScript для новичков. Занятие 7.

На данный момент при клике на любую из четырех наших кнопок мы просто получаем одно и то же сообщение в консоли. Однако, мы знаем, что каждая кнопка должна будет пополнять разный счетчик характеристики. Другими словами, функция, которую мы запускаем на событие “click” хоть и будет иметь одинаковый алгоритм (возвращение свойству width значения 100), но делать это для разных счетчиков. Давайте еще раз взглянем на html-структуру наших кнопок в браузере, чтобы теперь выяснить чем они отличаются.

Обратите внимание, каждый html-элемент нашей кнопки имеет атрибут data-state. Он одинаков для кнопки «Отправить гулять» и «Покормить», что нам подходит, так как согласно нашей схеме эти две кнопки будут отвечать за пополнение одного и того же счетчика. А у двух оставшихся этот атрибут имеет уникальное значение. Мы можем воспользоваться этим атрибутом для увязывания кнопок с нашими счетчиками. Для начала давайте извлечем этот атрибут, чтобы при клике на кнопку у нас выводилось не одно и то же сообщение, а конкретное.

var Booth = {
    informer: {
        selector: document.getElementById('info'),
        setText: function(text) {
            this.selector.innerText = text;
        }
    },
    timer1: {
        selector: document.getElementById('fatigue'),
        isOrange: false,
        isRed: false,
        width: 100
    },
    timer2: {
        selector: document.getElementById('satiety'),
        isOrange: false,
        isRed: false,
        width: 100
    },
    timer3: {
        selector: document.getElementById('mood'),
        isOrange: false,
        isRed: false,
        width: 100
    },
    /* добавляем события к выбранным кнопкам */
    buttons: document.getElementsByClassName('monitor__btn'),
    addButtonsEvents: function() {
        for(var i = 0; i < this.buttons.length; i+=1) {
            this.buttons[i].addEventListener('click', function() {
                var action = this.getAttribute('data-state');
                console.log(action);
            });
        }
    },
    /* Запускаем счетчики парметров */
    runProgressBar: function(timer, seconds) {
        
        var milliseconds = seconds*1000;
        var interval = setInterval(function() {
            if(timer.width != 0) {
                timer.width -= 1;
                timer.selector.style.width = timer.width + '%';

                /* смена цвета счетчика*/

                if(timer.isOrange == false && timer.width <= 50) {
                    timer.isOrange = true;
                    timer.selector.setAttribute('class', 'monitor-bar__progress attention');
                }

                if(timer.isRed == false && timer.width <= 25) {
                    timer.isRed = true;
                    timer.selector.setAttribute('class', 'monitor-bar__progress alarm');
                }

            } else {
                clearInterval(interval);
            }
        }, 200);
    },
    /* пополняем счетчик */
    refreshProgressBar: function(timer) {
        timer.width = 100;
        timer.selector.setAttribute('class', 'monitor-bar__progress');
    }
}

Обратите внимание на наши преобразования в методе addButtonsEvents(). Мы завели переменную action для того чтобы сохранить в ней значение атрибута data-action. Значение этого атрибута ы получаем с помощью метода getAttribute(). Этот метод – собрат метода setAttribute(), который мы уже использовали ранее. Только он не устанавливает нужный нам атрибут к html-элементу, а ищет внутри него свойство, которое мы передаем ему как параметр, и возвращает нам его значение (при условии, что свойство действительно есть у html-элемента). Далее мы просто выводим в консоль, полученное нами значение. Обратите так же внимание на то, что мы снова используем контекст this. В момент клика, этот контекст будет указывать кнопку, по которой этот клик был совершён. Давайте откроем браузер и посмотрим, что же мы получили:

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

var Booth = {
    informer: {
        selector: document.getElementById('info'),
        setText: function(text) {
            this.selector.innerText = text;
        }
    },
    timer1: {
        selector: document.getElementById('fatigue'),
        isOrange: false,
        isRed: false,
        width: 100
    },
    timer2: {
        selector: document.getElementById('satiety'),
        isOrange: false,
        isRed: false,
        width: 100
    },
    timer3: {
        selector: document.getElementById('mood'),
        isOrange: false,
        isRed: false,
        width: 100
    },
    /* добавляем события к выбранным кнопкам */
    buttons: document.getElementsByClassName('monitor__btn'),
    addButtonsEvents: function() {
        for(var i = 0; i < this.buttons.length; i+=1) {
            this.buttons[i].addEventListener('click', function() {
                var action = this.getAttribute('data-state');
                if(action == 'fatigue') {
                    console.log('обновить счетчик Усталость - timer1');
                }
                if(action == 'satiety') {
                    console.log('обновить счетчик Сытость - timer2');
                }
                if(action == 'mood') {
                    console.log('обновить счетчик Настроение - timer3');                        
                }   
            });
        }
    },
    /* Запускаем счетчики парметров */
    runProgressBar: function(timer, seconds) {
        
        var milliseconds = seconds*1000;
        var interval = setInterval(function() {
            if(timer.width != 0) {
                timer.width -= 1;
                timer.selector.style.width = timer.width + '%';

                /* смена цвета счетчика*/

                if(timer.isOrange == false && timer.width <= 50) {
                    timer.isOrange = true;
                    timer.selector.setAttribute('class', 'monitor-bar__progress attention');
                }

                if(timer.isRed == false && timer.width <= 25) {
                    timer.isRed = true;
                    timer.selector.setAttribute('class', 'monitor-bar__progress alarm');
                }

            } else {
                clearInterval(interval);
            }
        }, 200);
    },
    /* пополняем счетчик */
    refreshProgressBar: function(timer) {
        timer.width = 100;
        timer.selector.setAttribute('class', 'monitor-bar__progress');
    }
}

Как видите, используя условные операторы if(…){…} Мы просто сравниваем значение нашего состояния и выводим сообщение о том какой именно счетчик должен быть пополнен. Проверим:

Как видим, теперь каждая кнопка «знает» какой именно счетчик она должна будет пополнить. Дело осталось за малым, вместо вывода сообщения вызвать наш метод для обновления счетчика refreshProgressBar() и передать ему нужный счетчик как параметр.

var Booth = {
    informer: {
        selector: document.getElementById('info'),
        setText: function(text) {
            this.selector.innerText = text;
        }
    },
    timer1: {
        selector: document.getElementById('fatigue'),
        isOrange: false,
        isRed: false,
        width: 100
    },
    timer2: {
        selector: document.getElementById('satiety'),
        isOrange: false,
        isRed: false,
        width: 100
    },
    timer3: {
        selector: document.getElementById('mood'),
        isOrange: false,
        isRed: false,
        width: 100
    },
    /* добавляем события к выбранным кнопкам */
    buttons: document.getElementsByClassName('monitor__btn'),
    addButtonsEvents: function() {
        var context = this; // равен объекту Booth

        for(var i = 0; i < this.buttons.length; i+=1) {
            this.buttons[i].addEventListener('click', function() {
                var action = this.getAttribute('data-state');
                if(action == 'fatigue') {
                    context.refreshProgressBar(context.timer1);
                }
                if(action == 'satiety') {
                    context.refreshProgressBar(context.timer2);
                }
                if(action == 'mood') {
                    context.refreshProgressBar(context.timer3);                     
                }   
            });
        }
    },
    /* Запускаем счетчики парметров */
    runProgressBar: function(timer, seconds) {
        
        var milliseconds = seconds*1000;
        var interval = setInterval(function() {
            if(timer.width != 0) {
                timer.width -= 1;
                timer.selector.style.width = timer.width + '%';

                /* смена цвета счетчика*/

                if(timer.isOrange == false && timer.width <= 50) {
                    timer.isOrange = true;
                    timer.selector.setAttribute('class', 'monitor-bar__progress attention');
                }

                if(timer.isRed == false && timer.width <= 25) {
                    timer.isRed = true;
                    timer.selector.setAttribute('class', 'monitor-bar__progress alarm');
                }

            } else {
                clearInterval(interval);
            }
        }, 200);
    },
    /* пополняем счетчик */
    refreshProgressBar: function(timer) {
        timer.width = 100;
        timer.selector.setAttribute('class', 'monitor-bar__progress');
    }
}

В этом преобразовании ест только один момент, требующий разъяснения. Это момент с контекстом this.
Как мы помним внутри нашей функции при клике this указывает на текущую кнопку, и мы бы не смогли использовать наш метод refreshProgressBar() метод через этот this, так как логично у выборки кнопки нет такого метода. Поэтому мы использовали прием, которое называется сохранение контекста. Перед тем как мы в принципе начали использовать метод addEventListener() мы создали переменную context и сохранили в нее значение this, которое на этом уровне ссылается на наш объект {Booth}. И далее мы уже используем этот контекст.

Теперь, чтобы убедится, что все работает нам надо запустить все три наших счетчика, и в процессе их уменьшения понажимать на наши кнопки. Я предлагаю вынести запуск всех трех наших счётчиков в отдельный метод. И не забудем убрать наше жестко указанное время в интервале, чтобы мы могли сами указать нужный нам интервал.

Так мы вынесли запуск наших счетчиков в отдельный метод startTimers() и создали метода startGmae() где сначала добавляем события к нашим кнопкам, а затем вызывем метод со стартом наших счетчиков.

А тут:

мы убрали жестко прописанные 200 миллисекунд, и ернули возможность самим выставлять нужное нам время.

var Booth = {
    informer: {
        selector: document.getElementById('info'),
        setText: function(text) {
            this.selector.innerText = text;
        }
    },
    timer1: {
        selector: document.getElementById('fatigue'),
        isOrange: false,
        isRed: false,
        width: 100
    },
    timer2: {
        selector: document.getElementById('satiety'),
        isOrange: false,
        isRed: false,
        width: 100
    },
    timer3: {
        selector: document.getElementById('mood'),
        isOrange: false,
        isRed: false,
        width: 100
    },
    /* добавляем события к выбранным кнопкам */
    buttons: document.getElementsByClassName('monitor__btn'),
    addButtonsEvents: function() {
        var context = this; // равен объекту Booth

        for(var i = 0; i < this.buttons.length; i+=1) {
            this.buttons[i].addEventListener('click', function() {
                var action = this.getAttribute('data-state');
                if(action == 'fatigue') {
                    context.refreshProgressBar(context.timer1);
                }
                if(action == 'satiety') {
                    context.refreshProgressBar(context.timer2);
                }
                if(action == 'mood') {
                    context.refreshProgressBar(context.timer3);                     
                }   
            });
        }
    },
    /* Запускаем счетчики парметров */
    runProgressBar: function(timer, seconds) {
        
        var milliseconds = seconds*1000;
        var interval = setInterval(function() {
            if(timer.width != 0) {
                timer.width -= 1;
                timer.selector.style.width = timer.width + '%';

                /* смена цвета счетчика*/

                if(timer.isOrange == false && timer.width <= 50) {
                    timer.isOrange = true;
                    timer.selector.setAttribute('class', 'monitor-bar__progress attention');
                }

                if(timer.isRed == false && timer.width <= 25) {
                    timer.isRed = true;
                    timer.selector.setAttribute('class', 'monitor-bar__progress alarm');
                }

            } else {
                clearInterval(interval);
            }
        }, milliseconds);
    },
    /* пополняем счетчик */
    refreshProgressBar: function(timer) {
        timer.width = 100;
        timer.selector.setAttribute('class', 'monitor-bar__progress');
    },
    /* Запускаем наши счетчики */
    startTimers: function() {
        this.runProgressBar(this.timer1, 0.3);
        this.runProgressBar(this.timer2, 0.5);
        this.runProgressBar(this.timer3, 0.7);
    },
    /* Запускаем игру */
    startGame: function() {
        this.addButtonsEvents();
        this.startTimers();
    }
}

Теперь проверим все это в браузере:

Как видим при вызове нашего метода startGame() наши счетчики начали уменьшаться. А при нажатии на кнопку, мы получаем пополнение конкретного счетчика.

А на сегодня все. Продолжение следует…

Ссылки на предыдущие занятия:

Практика JavaScript для новичков. Занятие 1,
Практика JavaScript для новичков. Занятие 2,
Практика JavaScript для новичков. Занятие 3,
Практика JavaScript для новичков. Занятие 4,
Практика JavaScript для новичков. Занятие 5,
Практика JavaScript для новичков. Занятие 6

Ссылки на предыдущий курс:

Урок 1 - Окружение.,
Урок 2 - Некоторые особенности синтаксиса.,
Урок 3 - Переменные.,
Урок 4 - Типы переменных, как основа для их взаимодействия.,
Урок 5 - Операции с переменными одного типа.,
Урок 6 - Операции с переменными одного типа. Часть II.,
Урок 7 - Взаимодействие переменных с разными типами.,
Урок 8 - Взаимодействие переменных разного типа. часть II.,
Урок 9 - Взаимодействие переменных разного типа. Часть III.,
Урок 10 - Другие возможные взаимодействия между переменными.,
Урок 11 - Другие возможные взаимодействия между переменными. Часть II.,
Урок 12 - Другие возможные взаимодействия между переменными. Операторы присваивания.,
Урок 13 - Другие возможные взаимодействия между переменными. Операторы сравнения.,
Урок 14 - Сложные переменные. Array и Object.,
Урок 15 - Условные операторы.,
Урок 16 - Циклы.,
Урок 17 - Циклы. Часть II.,
Урок 18 - Функции.,
Урок 19 - Функции. Часть II.,
Урок 20 - Профилирование. Функции, часть III.,
Урок 21 - Функции, Часть IV. Аргументы.,
Урок 22 - Objects (Объекты).,
Урок 23 - Встроенные функции и объекты.,
Урок 24 - Встроенные функции и Объекты, Часть II. Глобальные функции и переменные.,
Урок 25 - Встроенные функции и Объекты, Часть III. Document Object Model.,
Урок 26 - Встроенные функции и Объекты, Часть III. Document Object Model.
Урок 27 - Встроенные объекты. Объект Style, Events, Часть II.
Урок 28 - Встроенная переменная this. Глобальная и локальная области видимости.
Урок 29 - Объектно-ориентированное Программирование. Введение.
Урок 30 - Объектно-ориентированное Программирование. Часть II. Полиморфизм.
Урок 31 - OОП. Наследование, Часть I. Оператор new.
Урок 32 - ООП. Наследование, Часть II. PROTOTYPE.
Урок 33 - ООП. Часть II. Полиморфизм.
Урок 34 - ООП. Часть III. Инкапсуляция.

Авторы получают вознаграждение, когда пользователи голосуют за их посты.
Голосующие читатели также получают вознаграждение за свой голос.
Порядок сортировки:  Популярное
69
  ·  в прошлом году

@rassen Поздравляю! Вы добились некоторого прогресса на Голосе и были награждены следующими новыми бейджами:

Вы опубликовали пост каждый день недели

Вы можете нажать на любой бейдж, чтобы увидеть свою страницу на Доске Почета.
Чтобы увидеть больше информации о Доске Почета, нажмите здесь

Если вы больше не хотите получать уведомления, ответьте на этот комментарий словом стоп

Голосуя за это уведомление, вы помогаете всем пользователям Голоса. Узнайте, как здесь.

69
  ·  в прошлом году

@rassen Поздравляю! Вы добились некоторого прогресса на Голосе и были награждены следующими новыми бейджами:

Награда за количество полученных голосов

Вы можете нажать на любой бейдж, чтобы увидеть свою страницу на Доске Почета.
Чтобы увидеть больше информации о Доске Почета, нажмите здесь

Если вы больше не хотите получать уведомления, ответьте на этот комментарий словом стоп

Голосуя за это уведомление, вы помогаете всем пользователям Голоса. Узнайте, как здесь.

73
  ·  в прошлом году

Ваш пост поддержали следующие Инвесторы Сообщества "Добрый кит":
@dreamer, @dimarss, @alisaroma, @exan, @zazazum, @ani.vartanova, @m0rte, @seagull15
Поэтому я тоже проголосовал за него!
Узнать подробности о сообществе можно тут:
Разрешите представиться - Кит Добрый
Правила
Инструкция по внесению Инвестиционного взноса
Вы тоже можете стать Инвестором и поддержать проект!!!


Если Вы хотите отказаться от поддержки Доброго Кита, то ответьте на этот комментарий командой "!нехочу"