Форум

Методологія

Інструментарій

Платформа

Спільнота

JavaScript по БЕМ

У БЕМ-методології JavaScript використовується для «пожвавлення» веб-сторінки і розглядається як одна з технологій реалізації блоку.

У БЕМ до JavaScript застосовуються додаткові правила, які дозволяють реалізувати всі ідеї компонентного підходу БЕМ-методології.

Основні принципи компонентного підходу в JavaScript по БЕМ

JavaScript — це одна з технологій реалізації блоку, тому в роботі з JavaScript можуть дотримуватися основні ідеї БЕМ-методології:

Єдина предметна область

У веб-розробці фінальний продукт (наприклад, веб-сторінка) складається з різних технологій (HTML, CSS, JS і т. д.). У БЕМ для роботи у всіх технологіях використовуються єдині терміни та підходи до реалізації. Таким чином вся команда БЕМ-проекту отримує єдиний мову для спілкування, тобто працює в термінах блоків, елементів і модифікаторів.

Так, JavaScript-реалізація блоків не оперує поняттями DOM-елементів, а використовує наступний рівень абстракції — БЕМ-дерево. Це дозволяє не спиратися на класи, а незалежно описувати поведінку блоків та їх опціональних елементів. Модифікатори JavaScript використовуються для вираження логіки роботи блоку або елемента (за аналогією з CSS, де з допомогою модифікаторів задається зовнішній вигляд). Поведінка блоків і елементів описується в JavaScript як набір станів.

Застосування єдиних понять у всіх технологіях дозволяє реалізувати в JavaScript різні хелпери для роботи з компонентами і відмовитися від жорсткого кодування імен блоків і роздільників. Такий підхід дає можливість, наприклад, знайти всередині блоку всі елементи з певним ім'ям і виставити їм модифікатор, перевірити його значення.

Приклад

Розглянемо приклад спливаючого вікна (popup). Показувати спливаюче вікно можна різними способами:

  • Скористатися поширеним рішенням і додавати відповідний клас. Такий спосіб не завжди зручний, так як необхідно жорстко прописувати ім'я блоку в коді.

    document.querySelector('.button')
      .addEventListener('click', function() {
        document.querySelector('.popup').classList.toggle('popup_visible');
    }, false);
    
    
  • Скористатися принципами БЕМ і оперувати не класами, а блоками, елементами та модифікаторами. У такому випадку пошук компонента виконується не по класу, а по імені блоку, який у проекті може виражатися не тільки класом, але і тегом, атрибутом і т. д. Відображення спливаючого вікна (переклад блоку popup стан visible) також здійснюється не по класу, а з допомогою модифікатора.

    block('button').click(function() {
        block('popup').toggleMod('visible');
    });
    

Зверніть увагу! Для прикладів, написаних за БЕМ-методології, що використовується псевдокод. Реальні приклади реалізації представлені у документації до i-bem.js.

Використання єдиної предметної області дає можливість на більш високому рівні взаємодіяти з компонентами.

Робота з модифікаторами

Модифікатори можуть задавати блокам певні стани. Логіка роботи блоку реалізується в JavaScript і описується за допомогою станів. Переклад блоку в інший стан може проводитися за допомогою установки/зняття модифікатора. Зміна модифікатора створює подія, яку можна використовувати для роботи з блоком.

Наприклад, щоб відзначити чекбокс, блоку checkbox потрібно встановити модифікатор checked значення true.

У БЕМ-проекті не можна змінювати стану в режимі runtime з допомогою модифікатора, безпосередньо змінюючи CSS-клас на відповідному DOM-сайті. Для коректної роботи JavaScript всі маніпуляції з модифікаторами повинні проводитися за допомогою методів-хелперів.

Приклади реалізації доступні в документації до i-bem.js.

Реакція на зміну модифікаторів

Перехід блоку з одного стану в інший часто викликає зміни в його зовнішньому вигляді. Якщо в CSS зовнішній вигляд блоку задається з допомогою модифікатора, то зміна стану блоку, викликане тим же модифікатором, автоматично застосує всі необхідні стилі.

У БЕМ реакція на встановлення/зняття модифікатора описується декларативно. Так, наприклад, якщо в CSS під час виконання з'являється якийсь додатковий клас (модифікатор), то всі властивості цього модифікатора автоматично застосовуються до DOM-вузла, на який цей клас встановлений. В JavaScript відбувається те ж саме: якщо з'являється модифікатор (додається новий клас до DOM-сайту), то вся функціональність, властива цьому модификатору, що застосовується. Якщо модифікатор зникає, функціональність відключається.

Щоб динамічно змінювати стану блоків і елементів, використовуються спеціальні методи для встановлення і зняття модифікаторів.

Приклади реалізації доступні в документації до i-bem.js.

Приклад

Розглянемо форму відправлення повідомлення. Повинно виконуватися умова: якщо введений неправильний email, кнопка відправки (блок button) стає недоступною (отримує модифікатор button_disabled).

Можна жорстко прописати всі умови в коді і постійно виконувати перевірку. Такий підхід не зручний, так як будь-яка зміна вимагає змін у коді вручну.

Можна задекларувати поведінка блоку і отримати можливість перекривати кожен модифікатор окремо на новому рівні перевизначення. В декларації можна вказати, як блок або елемент повинен відреагувати на зміну модифікатора.

block('button').onSetMod({
    focused: {
        true: this.onFocus,
        '': this.onBlur
    }
});

Такий підхід дає можливість:

  • Реагувати на модифікатор незалежно від способу його установки/зняття (через JavaScript API: block('button').setMod('focused') або користувач встановив/зняв фокус курсором).
  • Визначати кожному станом свій зовнішній вигляд, додавши стилі модификатору.
  • Змінювати або повністю перекривати поведінка блоку з допомогою рівнів перевизначення.

Поділ на частини коду

До JavaScript можуть застосовуватись основні принципи організації і зберігання коду за БЕМ-методології:

  • поділ коду на окремі частини — логіка роботи кожного блоку, його опціональних елементів і модифікаторів описується в окремих файлах;
  • JavaScript-файли для кожного компонента зберігаються відповідно до правилами організації файлової структури БЕМ-проекту.

Приклад

Розглянемо приклад логотипу (блок logo), реалізованого в двох технологіях: шаблоні і стилях.

HTML-реалізація блоку:

<a class="logo" href="/">Ваша крута компанія</a>

CSS-реалізація блоку:

.logo {
    width: 150px;
    height: 100px;
}

Блок logo у файловій структурі проекту:

logo/
    logo.css   # Зовнішній вигляд блоку
    logo.tmpl  # Шаблони для генерації HTML-представлення блоку

Додамо блоку logo JavaScript-функціональність: тепер натискання на логотип викликає якусь дію. Згідно БЕМ-методології нове поведінка блоку logo буде реалізовано наступним чином:

  • в окремому файлі;
  • назва файлу повинна відповідати імені блоку з розширенням .js;
  • файл logo.js буде знаходиться в директорії блоку logo/.

JavaScript-реалізація блоку:

document.querySelector('.logo').addEventListener('click', doSomething, false);

Файл logo.js у файловій структурі блоку:

logo/
    logo.css   # Зовнішній вигляд блоку
    logo.tmpl  # Шаблони для генерації HTML-представлення блоку
    logo.js    # Динамічну поведінку блоку в браузері

Поділ коду на частини і сувора організація файлової структури проекту дозволяє не тільки полегшити навігацію по проекту і повторне використання або перенесення компонентів, але і працювати з рівнями перевизначення для JavaScript і використовувати збірку.

Робота з рівнями перевизначення

В описі БЕМ-методології приведено багато прикладів, де кінцева CSS-реалізація блоку збирається з різних рівнів перевизначення. Застосування принципів БЕМ-методології до JavaScript дозволяє аналогічним чином розділяти поведінка блоків за різними рівнями:

  • реалізовувати нову функціональність блоку на іншому рівні перевизначення, зберігаючи попереднє поведінка блоку, успадковувати і доповнювати його (робити super call);
  • повністю перекривати поведінка блоку (змінити);
  • додавати нові блоки з новою функціональністю, яких не було на попередніх рівнях.

З допомогою рівнів перевизначення можна створити універсальну JavaScript-бібліотеки блоків і змінювати її на проектному рівні. Потім використовувати збірку і включати в проект тільки необхідну поведінку блоків.

Приклад

Повернемося до прикладу форми надсилання повідомлення:

block('button').onSetMod({
    focused: {
        true: this.onFocus,
        '': this.onBlur
    }
});

Запис у стилі БЕМ дозволяє:

  • Повністю перекривати поведінка блоку на іншому рівні перевизначення.

    block('button').onSetMod({
        focused: {
            true: this.someCustomOnFocused
        }
    });
    
  • Додавати або частково змінювати поведінку блоку на іншому рівні перевизначення.

    block('button').onSetMod({
        focused: {
            true: function() {
                this.__base.apply(this, arguments);
                this.someCustomOnFocused();
            }
        }
    });
    
    

Для роботи з рівнями перевизначення БЕМ можна використовувати спеціалізований фреймворк, наприклад, i-bem.js, так як він створений за вимогами БЕМ-методології.

Як перейти на JavaScript з БЕМ

Найшвидший шлях — почати застосовувати принципи БЕМ-методології в своєму проекті і отримувати перші результати без використання спеціалізованого фреймворка. Як це зробити на практиці показано в статті БЕМ — це не тільки про CSS з прикладами на jQuery.

Щоб реалізувати всі ідеї БЕМ у вашому проекті, необхідно:

  • працювати у єдиних термінах блоків, елементів і модифікаторів у всіх технологіях;
  • створювати незалежні компоненти — блоки — на рівні JavaScript;
  • змінювати поведінку блоків, елементів і модифікаторів з допомогою рівнів перевизначення за аналогією з CSS;
  • повторно використовувати блоки, переносити їх між проектами;
  • полегшити і прискорити розробку і налагодження проекту за рахунок незв'язаності компонентів і можливості розробляти поблочно;
  • включати в збірку тільки потрібну JavaScript-реалізацію блоку;
  • полегшити навігацію по файловій структурі проекту.

Особливості реалізації JavaScript за БЕМ-методології

Декларативний стиль

Декларативність JavaScript у БЕМ-проекті проявляється в наступному:

  • Поведінка кожного блоку описується незалежно.
  • Стану блоку задаються декларативно. При зміну станів автоматично викликається код, який задекларований для цього стану.
  • Логіка роботи блоку описується як набір дій і умов, при яких ці дії необхідно виконувати. Це дозволяє розділяти функціональність блоку на окремі частини і використовувати рівні перевизначення.

Докладніше про застосування рівнів переопределния в JavaScript

Принципи ООП в JavaScript по БЕМ

У БЕМ-методології до JavaScript застосовуються основні принципи об'єктно-орієнтованого програмування (ООП).

Інкапсуляція

У БЕМ JavaScript-реалізація одного блоку відокремлена від іншого. Кожен блок надає API для взаємодії з іншими блоками.

Декларація блоку дозволяє приховати його внутрішню реалізацію. Так як елементи завжди є внутрішньою реалізацією блоку, звернення до них можливо тільки через API самого блоку.

Спадкування

Декларативне опис поведінки блоків дозволяє використовувати методи базового блоку всередині похідного, наслідувати їх. Новий блок може отримувати всі властивості і методи базового.

Також можна створювати ланцюжки спадкування — блок успадковується від іншого, який, у свою чергу, успадковується від третього і т. д.

Приклади реалізації доступні в документації до i-bem.js.

Подання динамічних блоків в DOM

Блокам з JavaScript-реалізацією можуть відповідати вузли в HTML. У цьому випадку йдеться про те, що блоки мають DOM-подання.

У найпростішому випадку блок відповідає DOM-вузла один до одного. Однак DOM-вузол і блок — це не завжди одне і те ж. Можна розмістити кілька блоків на одному DOM-сайті (це називається мікс), а також реалізувати один блок на декількох DOM-вузлах.

Існують блоки без DOM-подання. В JavaScript вони представлені у вигляді об'єктів, що мають свої методи.

Приклади реалізації доступні в документації до i-bem.js.

Взаємодія блоків

БЕМ-методологія передбачає роботу з незалежними блоками. Однак на практиці повна незалежність блоків недосяжна.

Блоки можуть взаємодіяти один з одним за допомогою:

  • Підписки на події інших примірників блоків.
  • Підписки на зміни модифікаторів.
  • Безпосереднього виклику методів інших примірників блоків або статичних методів класу іншого блоку.
  • Будь патернів взаємодії. Наприклад, каналу подій: всі комунікації відбуваються завдяки повідомленнями, які компоненти публікують і слухають з допомогою посередника.

Приклади реалізації доступні в документації до i-bem.js.

БЕМ-методологія рекомендує вибудовувати взаємодію між блоками в ієрархічному порядку відповідно до їх розташуванням в DOM-дереві. Вкладений блок не повинен нічого знати про батьківському блоці, так як це порушує принцип незалежності компонентів.

Взаємодія блоку з його елементами

Елемент — це внутрішня реалізація блоку. У БЕМ-методології прийнято реалізовувати додаткові хелпери блоку для роботи з його елементами. Звернення безпосередньо до елементу іншого блоку неможливо. Взаємодія з елементом відбувається тільки через API блоку, якому належить даний елемент.

Якщо ви помітили помилку, або хочете доповнити статтю, ви завжди можете або написати нам про це на Гітхабі, або поправити статтю з допомогою prose.io.