Форум

Методология

Инструментарий

Платформа

Сообщество

JavaScript по БЭМ

В БЭМ-методологии JavaScript — это одна из технологий реализации блока. Поэтому к JavaScript наравне с CSS применяются основные принципы методологии БЭМ:

Особенности реализации JavaScript по БЭМ-методологии рассматриваются в следующих разделах:

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

Для работы с JavaScript наравне другими технологиями используются термны блоков, элементов и модификаторов.

В БЭМ к JavaScript применяется компонентный подход, который в отличие от Web Components не требует совместимости с браузерами. Независимым компонентом является блок. Аналогом Shadow Dom выступают элементы блока, которые реализуют принцип инкапсуляции. Модификаторы блоков и элементов отвечают за состояния. Установка/снятие модификатора изменяет поведение блока.

Таким образом в БЭМ используется абстракция над DOM-деревом — БЭМ-дерево.

Рассмотрим пример, в котором необходимо показать всплывающее окно (блок popup).

Один из наиболее распространенных способов — добавить соответствующий класс и жестко прописать имя блока в коде.

document.querySelector('.button').addEventListener('click', function() {
    document.querySelector('.popup').classList.toggle('popup_visible');
}, false);

В БЭМ-проекте нет необходимости хардкодить имя блока. Поиск компонента выполняется по имени блока, так как имя всегда уникально. Сам компонент может выражаться классом, тегом, атрибутом и т.д. Отображение всплывающего окна также не зависит от класса: блок переводится в состояние visible с помощью добавления модификатора.

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

Важно Для примеров, написанных по БЭМ-методологии, используется псевдокод. Реальные примеры реализации представлены в документации к i-bem.js.

Привязка JavaScript-блоков к HTML

Первичным «каркасом» страницы является HTML-дерево документа. Блокам с JavaScript-реализацией могут соответствовать узлы в HTML с именем блока в атрибуте class. В этом случае говорится о том, что блоки имеют DOM-представление.

Такой способ привязки JavaScript-компонентов к DOM-дереву имеет следующие преимущества:

  • естественная деградация интерфейса на клиентах с отключенным JavaScript;
  • прогрессивный рендеринг — возможность начинать отрисовку элементов интерфейса до окончания загрузки всех данных страницы.

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

Разделение кода на части

В БЭМ-методологии поведение каждого блока описывается независимо. Независимость блоков в JavaScript позволяет повторно использовать блоки и реализуется за счет использования принципов:

Логика работы каждого блока, его опциональных элементов и модификаторов описывается в отдельных файлах. JavaScript-файлы хранятся в соответствии с правилами организации файловой структуры БЭМ-проекта.

Рассмотрим на примере логотипа (блок logo), реализованного в технологии CSS.

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

logo/           # Директория блока logo
    logo.css    # Реализация блока logo в технологии CSS

Добавим блоку logo новую функциональность: нажатие на логотип будет вызывать какое-то действие.

JavaScript-реализация блока logo:

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

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

logo/           # Директория блока logo
    logo.css    # Реализация блока logo в технологии CSS
    logo.js     # Реализация блока logo в технологии JavaScript

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

Принцип инкапсуляции

В БЭМ внутренняя реализация блока скрыта. Блок предоставляет API для взаимодействия с другими блоками. Таким образом достигается независимость блока, возможность его повторного использования.

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

Принцип декларативности

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

Принцип наследования

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

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

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

Работа с уровнями переопределения

Декларативное описание работы блока обеспечивает возможность использовать уровни переопределения для JavaScript наравне с CSS:

  • реализовывать новое поведение блока на другом уровне переопределения, сохраняя предыдущее, наследовать и дополнять его (делать super call);
  • полностью перекрывать поведение блока (переопределять);
  • добавлять блоки с новым поведением в проект.

С помощью уровней переопределения можно создать универсальную JavaScript-библиотеку блоков, изменять ее на проектном уровне и включать в проект только необходимое поведение блоков.

Подробнее о подключении библиотеки в проект.

Рассмотрим пример формы отправки сообщения.

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

Запись в БЭМ-терминах позволяет:

  • Полностью перекрывать поведение блока на другом уровне переопределения.

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

    block('button').onSetMod({
        focused: {
            true: function() {
                this.__base.apply(this, arguments); // Вызываем предыдущую реализацию
                this.someCustomOnFocused();
            }
        }
    });
    
    

Для работы с JavaScript в БЭМ-терминах и использования уровней переопределения в БЭМ создан специализированный фреймворк i-bem.js.

Работа с блоками

Взаимодействие блоков

БЭМ-методология предполагает работу с независимыми блоками. Однако на практике полная независимость блоков недостижима.

Блоки могут взаимодействовать друг с другом с помощью:

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

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

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

Взаимодействие блока с его элементами

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

Работа с модификаторами

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

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

Для корректной работы JavaScript в БЭМ-проекте все манипуляции с модификаторами должны производиться при помощи методов-хелперов. Изменять значение модификаторов следует с помощью спец методов, а не менять напрямую CSS-класс на соответствующем DOM-узле.

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

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

В БЭМ реакция на установку/снятие модификатора описывается декларативно: изменение состояния автоматически вызывает код, который задекларирован для этого состояния. Если появляется модификатор (добавляется новый класс к DOM-узлу), то вся функциональность, свойственная этому модификатору, также применяется. Если модификатор исчезает, функциональность отключается.

Переход блока из одного состояния в другое часто сопровождается изменениями его внешнего вида. Так как добавление модификатора блоку изменяет его класс на DOM-узле, а стили написаны опираясь только на классы, изменение класса автоматически приводит к изменению внешнего вида блока.

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

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

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

Один из способов решения этой задачи — жестко прописать все условия в коде и постоянно выполнять проверку. Такой подход неудобен, так как любое изменение потребует изменений в коде вручную.

Другой способ — использовать методологию БЭМ и задекларировать поведение блока таким образом, чтобы получить возможность перекрывать каждый модификатор отдельно на новом уровне переопределения. В декларации можно указать, как блок или элемент должен отреагировать на изменение модификатора.

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

Такой подход дает возможность:

  • Определять каждому состоянию свой внешний вид, добавив стили модификатору.
  • Изменять или полностью перекрывать поведение блока с помощью уровней переопределения.

Как перейти на JavaScript по БЭМ

Чтобы реализовать принципы БЭМ в проекте, необходимо:

  • работать в единых терминах блоков, элементов и модификаторов во всех технологиях;
  • создавать независимые компоненты (блоки) на уровне JavaScript;
  • описывать поведение блока как набор действий и условий их выполнения;
  • обращаться к элементам блока только через API самого блока и не нарушать принцип инкапсуляции;
  • изменять поведение блоков, элементов и модификаторов с помощью уровней переопределения по аналогии с CSS;
  • разделять код на мелкие независимые части для удобства работы с отдельными блоками;
  • повторно использовать блоки.

Чтобы начать использовать JavaScript по БЭМ в рабочем проекте, можно начать применять принципы БЭМ-методологии без использования специализированного фреймворка. Пример проекта с JavaScript по БЭМ на jQuery описан в статье БЭМ — это не только про CSS.

Если вы заметили ошибку или хотите чем-то дополнить статью, вы всегда можете или написать нам об этом на Гитхабе, или поправить статью с помощью prose.io.