Форум

Методология

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

Платформа

Сообщество

HTML по БЭМ

В БЭМ HTML-разметку можно создавать вручную или генерировать автоматически.

Разметка страницы описывается в терминах блоков, элементов и модификаторов. Применение единой предметной области во всех технологиях вносит свои особенности в организацию HTML-кода:

Привязка блоков к DOM-узлу

В HTML имена блоков указываются в атрибуте class.

В простейшем случае одному DOM-узлу соответствует один блок:

<span class="menu"></span>

Бывают случаи, когда необходимо разместить нескольких БЭМ-сущностей на одном DOM-узле, чтобы совместить их стили и поведение. Для этого в значении атрибута class указываются имена БЭМ-сущностей, разделенные пробелом. Такой подход называется миксом.

Микс используется, например, чтобы добавить блоку или элементу модификатор. В примере ниже к стилям блока menu добавлены новые стили модификатора этого блока menu_theme_bright:

<span class="menu menu_theme_bright"></span>

Читать подробнее

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

Читать подробнее

Вложенность элементов

Элементы можно вкладывать друг в друга. Допустима любая вложенность элементов.

Важно! Правила именования запрещают отражать иерархию в названии элемента (block__elem1__elem2). Читать подробнее

В примере ниже пункты меню представлены ссылками. Такая структура блока реализуется за счет вложенности элементов:

<ul class="menu">
    <li class="menu__item">
        <a class="menu__link" href="https://">...</a>
    </li>
</ul>

Использование HTML-оберток

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

Расположение блока относительно других блоков

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

В примере блоки header и footer позиционируются на странице с помощью микса с элементами блока page, которым заданы нужные стили.

HTML-реализация:

<body class="page">
    <!-- верхний колонтитул и навигация -->
    <header class="header page__header">...</header>
    <!-- нижний колонтитул -->
    <footer class="footer page__footer">...</footer>
</body>

CSS-реализация:

.page__header {
    padding: 20px;
}

.page__footer {
    padding: 50px;
}

Позиционирование элементов внутри блока

Для позиционирования вложенных HTML-элементов используется дополнительный элемент блока (например, block__inner).

В примере блок header позиционируется относительно страницы (блок page) с помощью стилей элемента page__inner.

HTML-реализация:

<body class="page">
    <div class="page__inner">
        <!-- верхний колонтитул и навигация -->
        <header class="header">...</header>
        <!-- нижний колонтитул -->
        <footer class="footer">...</footer>
    </div>
</body>

CSS-реализация:

.page__inner {
    margin: auto;
    width: 960px;
}

Автоматическая генерация HTML

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

Шаблоны — это технология реализации блока, результатом работы которой является HTML этого блока. С помощью шаблонов текущий HTML-элемент может быть подменен на другой или дополнен новыми.

В БЭМ-платформе разработана технология для создания шаблонов — BEMHTML. Все примеры в этом разделе приведены с использованием этого шаблонизатора.

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

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

Шаблоны описываются в терминах блоков, элементов и модификаторов. Поэтому для работы с шаблонами используется дополнительный уровень абстракции над DOM-деревом — БЭМ-дерево. БЭМ-дерево описывает имена БЭМ-сущностей, их состояния, порядок и вложенность. На основании этих данных шаблонизатор строит дерево узлов HTML-разметки блока.

БЭМ-дерево можно выразить любым форматом, который поддерживает древовидную структуру.

Пример DOM-дерева и соответствующего ему БЭМ-дерева:

<header class="header">
    <img class="logo">
    <form class="search-form">
        <input class="input">
        <button class="button"></button>
    </form>
    <ul class="lang-switcher">
        <li class="lang-switcher__item">
            <a class="lang-switcher__link" href="url">en</a>
        </li>
        <li class="lang-switcher__item">
            <a class="lang-switcher__link" href="url">ru</a>
        </li>
    </ul>
</header>

БЭМ-дерево:

header
    logo
    search-form
        input
        button
    lang-switcher
        lang-switcher__item
            lang-switcher__link
        lang-switcher__item
            lang-switcher__link

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

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

Шаблоны можно писать для всего блока и отдельно для элементов или модификаторов.

Пример файловой структуры блока menu:

menu/
    __item/
        menu__item.css
        menu__item.js
        menu__item.tmpl     # Шаблон элемента menu__item
    menu.css
    menu.js
    menu.tmpl               # Шаблон блока menu

Шаблон блока menu:

block('menu')(
  tag()('ul')               // Установлен тег <ul> для блока `menu`
);

Шаблон элемента menu__item:

block('menu').elem('item')(
  tag()('li')               // Установлен тег <li> для всех элементов `menu__item`
);

HTML-реализация блока menu, результат работы шаблонов:

<ul class="menu">
  <li class="menu__item">...</li>
  <li class="menu__item">...</li>
</ul>

Использование уровней переопределения и сборки

Шаблоны пишутся декларативно. Такой подход позволяет использовать уровни переопределения и сборку:

Переопределение шаблона

Декларативная природа шаблонов в БЭМ позволяет изменять шаблоны с помощью уровней переопределения.

В примере рассмотрен шаблон блока menu из библиотеки, подключенной в проект на отдельный уровень переопределения:

project
    library.blocks/                 # Уровень переопределения с блоками библиотеки
        menu/                       # Блок menu из библиотеки
            __item/
                menu__item.tmpl     # Шаблон элемента menu__item
            menu.css
            menu.js
            menu.tmpl               # Шаблон блока menu
    common.blocks/                  

Шаблоны блока menu и элемента menu__item из библиотеки:

// Шаблон блока menu
block('menu')(
    tag()('ul'),
    attrs()(function() { ... }),
    addJs()(true),
    addMix()({ ... })
);

// Шаблон элемента menu__item
block('menu').elem('item')(
  tag()('li')
);

Блок menu в проекте представлен таким БЭМ-деревом:

menu
    menu__item
    menu__item

По БЭМ-дереву шаблонизатор по умолчанию формирует HTML-разметку в виде списка ul + li:

<ul class="menu">
  <li class="menu__item"><li>
  <li class="menu__item"><li>
</ul>

Чтобы переопределить шаблон и изменить связку ul + li на nav + a, необходимо:

  • создать файлы шаблонов на уровне проекта;

  • задать новое значение свойству tag.

Файловая структура проекта:

project
    library.blocks/                 # Уровень переопределения с блоками библиотеки
        menu/                       # Блок menu из библиотеки
            __item/
                menu__item.tmpl     
            menu.css
            menu.js
            menu.tmpl               
    common.blocks/                  # Уровень переопределения с блоками проекта
        menu/
            __item/
                menu__item.tmpl     # Переопределенный шаблон элемента menu__item
            menu.tmpl               # Переопределенный шаблон блока menu

Шаблоны с уровня common.blocks:

// Шаблон блока menu
block('menu')(
    // Переопределено только значения свойства tag
    tag()('nav')
);

// Шаблон элемента menu__item
block('menu').elem('item')(
  tag()('a')
);

В результате сборки изменения, внесенные в шаблон, применятся ко всем блокам menu и элементам menu__item в проекте:

// Шаблон блока menu с уровня библиотеки
block('menu')(
    tag()('ul'),
    attrs()(function() { ... }),
    addJs()(true),
    addMix()({ ... })
);

// Переопределенный шаблон блока menu
block('menu')(
    // Переопределено только значение свойства tag
    tag()('nav')
);

// Шаблон элемента menu__item с уровня библиотеки
block('menu').elem('item')(
  tag()('li')
);
// Переопределенный шаблон элемента menu__item
block('menu').elem('item')(
  tag()('a')
);

По аналогии с CSS нижнее правило переопределит верхнее. В результате применения шаблона HTML-код изменится на:

<nav class="menu">
  <a class="menu__item" href="...">...</a>
  <a class="menu__item" href="...">...</a>
</nav>

Добавление дополнительных HTML-элементов

С помощью шаблонов блоки можно изменять в runtime, например, добавлять новые HTML-элементы.

В проекте блок menu представлен таким БЭМ-деревом:

menu
    menu__item
    menu__item

Чтобы позиционировать элементы (menu__item) внутри блока (menu), необходимо создать служебный элемент, например, menu__inner. Новый элемент не относится к данным блока и служит только для добавления разметки. Поэтому его можно добавить в runtime, то есть при рендеринге блока в HTML.

В примере элемент menu__inner добавлен в шаблон с помощью JavaScript-кода:

block('menu')(
    tag()('menu'),
    // Добавлен элемент menu__inner
    content()(function() {
        return {
            elem: 'inner',
            content: this.ctx.content
        };
    })
);

// Установлен тег <li> для элемента menu__inner
elem('inner')(
    tag()('ul')
);

В результате выполнения шаблона элемент menu__inner будет добавлен как отдельный узел в HTML-дерево:

<menu class="menu">
    <ul class="menu__inner">           // добавлен новый элемент menu__inner
      <li class="menu__item"></li>
      <li class="menu__item"></li>
    </ul>
</menu>
Если вы заметили ошибку или хотите чем-то дополнить статью, вы всегда можете или написать нам об этом на Гитхабе, или поправить статью с помощью prose.io.