Верстка для самых маленьких. верстаем страницу по бэму

До установки модификатора

pure.bundles/
    006-before-set-mod/
        blocks/
            page/
            accordion-menu/
                accordion-menu.bemhtml.js
                accordion-menu.css
                accordion-menu.js
        006-before-set-mod.bemjson.js
        006-before-set-mod.html

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

Пример 006-before-set-mod (BEMJSON) демонстрирует такой случай на примере блока accordion-menu.

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

modules.define('accordion-menu',
        , function(provide, BEMDOM, $) {

provide(BEMDOM.decl(this.name, {

    onSetMod: {
        'js' : {
            'inited' : function() {
                this._current = this.findElem('item', 'current', true);

                this.bindTo('item', 'click', function(e) {
                    this.setMod($(e.currentTarget), 'current', true);
                });
            }
        }
    },

    onElemSetMod: {
        'item' : {
            'current' : {
                'true' : function(elem) {
                    this.delMod(this._current, 'current');

                    this._current = elem;
                }
            }
        }
    }

}));

});

ВАЖНО: Обратите внимание, что здесь используется jQuery. Для этого потребовалось изменить декларацию модуля. Библиотека bem-core использует модульную систему ymaps/modules. При описании модуля все зависимые модули должны быть явно указаны

Библиотека bem-core использует модульную систему ymaps/modules. При описании модуля все зависимые модули должны быть явно указаны.

Пример с меню становится более интересным, если у меню могут быть неактивные (disabled) пункты. Такой пункт меню не может быть в состоянии . Конечно, всегда можно добавить дополнительную проверку в callback на установку модификатора, но bem-core предлагает более элегантное решение. По аналогии со свойствами и можно воспользоваться свойствами и , чтобы сообщить блоку, что делать до установки модификатора. А если такой callback возвращает , это предотвратит установку модификатора.

modules.define('accordion-menu',
        , function(provide, BEMDOM, $) {

provide(BEMDOM.decl(this.name, {
    beforeElemSetMod: {
        'item' : {
            'current' : {
                'true' : function(elem) {
                    return !this.hasMod(elem, 'disabled');
                }
            }
        }
    },
    ...
}));

});

Здесь происходит проверка, активен ли элемент, и если нет — такой элемент не может получить модификатор .

Модификаторы

И вот сейчас начнется самое интересное (если оно не началось до этого!). Когда Вы выбираете название класса, стремитесь сделать этот элемент повторяемым, чтобы Вам не пришлось писать новые названия классов в других разделах сайта, если стили элементов одинаковы. Если необходимо модифицировать (изменить) стили конкретного элемента, можете использовать модификатор (конечно же!). Чтобы сделать это, добавьте двойной дефиз после элемента (или блока). Вот краткий пример


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

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

Вы возможно думаете «но ведь название класса такое длинное!». То, как я это вижу: названия классов БЭМ очень точные, понятные, просты для чтения внутри html и четко дают понять, для чего служат.

Что мне также нравится в БЭМ — это возможность использовать только одно название класса для каждого html тэга. Давайте посмотрим, как это работает для label. Стандартные селекторы:

против селекторов БЭМ:

Языки вроде Sass (Sass в особенности) помогают быстро иметь элементы с одними и теми же стилями с небольшими изменениями. В примере ниже не допускается дублирование стилей, но мы можем изменить только то, что нам нужно. Но больше всего мне нравится в БЭМ то, что мне не нужно объединять двусмысленные классы вроде «». Если Вы используете фреймворк вроде Foundation, Вы можете начать использовать миксины. Но для простого примера, давайте стилизуем те label, которые мы только что обозначили.

Как начать повторно использовать код и избежать взаимного влияния компонентов друг на друга

Проблема

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

Рассмотрим проблему на примере навигационного меню:

<ul class="nav">
    <li class="item"><a class="link">One</a></li>
    <li class="item"><a class="link">Two</a></li>
    <li class="item"><a class="link">Three</a></li>
</ul>

CSS-стили к пункту могут быть записаны как:

.item
{
    padding: 4px 10px;
    color: black;
}

Если на страницу понадобится добавить дополнительные компоненты, содержащие пункты, то появится еще один блок кода с классом , например:

<div class="snippets">
    <div class="item">
        <h2 class="title"></h2>
        <img class="thumb">
    </div>
</div>

В этом случае CSS может быть оформлен с помощью каскадов. Для этого достаточно доопределить правила, уже написанные для :

.item
{
    padding: 4px 10px;
    color: black;
}

.snippets .item
{
    color: red;
    font-size: 14px;
}

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

Использование каскадов связывает независимые компоненты интерфейса: нет возможности исправить один компонент, не затронув стили другого.

Решение

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

Запишем тот же код в соответствии с правилами именования БЭМ:

<ul class="nav">
    <li class="nav__item"><a class="nav__link">One</a></li>
    <li class="nav__item"><a class="nav__link">Two</a></li>
    <li class="nav__item"><a class="nav__link">Three</a></li>
</ul>
.nav__item
{
    padding: 4px 10px;
    color: black;
}

В таком случае добавление нового пункта на страницу будет выглядеть так:

<div class="snippets">
    <div class="snippets__item">
        <h2 class="snippets__title"></h2>
        <img class="snippets__thumb">
    </div>
</div>

Пункт будет иметь соответствующие только ему уникальные CSS-правила:

.snippets__item
{
    padding: 4px 10px;
    color: red;
    font-size: 14px;
}

Изменения в не влияют на , так как пункты получают уникальные имена благодаря пространству имен, заданному именем блока. Это позволяет формировать независимые CSS-правила для всех элементов блока.

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

Блоки и можно повторно использовать и перемещать по странице или проекту. Уникальность имен классов, основанная на правилах именования БЭМ, позволяет блокам не зависеть друг от друга.

Использование каскадов в БЭМ

Методология БЭМ допускает использование каскадов.

.nav_hovered .nav__link
{
    text-decoration: underline;
}
.nav_theme_islands .nav__item
{
    line-height: 1.5;
}

Соглашение о стандартных полях BEMJSON

  •  — имя блока

  •  — имя элемента

  •  — хеш модификаторов блока

  •  — хеш модификаторов элемента

  •  — дочерние узлы

  •  — БЭМ-сущности, которые нужно примиксовать

  •  — нужно ли добавлять классы и JS-параметры для самой БЭМ-сущности и её миксов

  •  — JS-параметры

  •  — хеш HTML-атрибутов

  •  — HTML-класс

  •  — HTML-тег

имя блока.

{ block: 'page' }

имя элемента.

{
    block: 'page',
    content: {
        
        elem: 'header'
    }
}

mods

хеш модификаторов блока. Ключами могут быть любые корректные ключи для JS-объектов. Значения могут быть типа или .

{
    block: 'tab',
    mods: { name: 'index', visible: true }
}

Результат шаблонизации:

<div class="tab tab_name_index tab_visible"></div>

будет проигнорирован если указаны и .

{
    block: 'control',
    
    mods: { type: 'nav' },
    elem: 'input',
    elemMods: { type: 'search' }
}

Результат шаблонизации:

<div class="control__input control__input_type_search"></div>

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

{
    block: 'page',
    elem: 'header',
    elemMods: { type: 'search' }
}

content

Дочерние узлы.

{
    block: 'page',
    content: 
}

Объект с полем является специальным типом возможных объектов в BEMJSON. Все другие поля в этом объекте будут проигнорированы. В поле ожидается строка, которая будет выведена без изменений.

{
    block: 'markup',
    content: {
        html: '<code>new Date();</code>'
    }
}

Результат шаблонизации:

<div class="markup"><code>new Date();</code></div>

mix

БЭМ-сущности, которые нужно к текущей.

{
    block: 'link',
    mix: { block: 'controller' }
}

Результат шаблонизации:

<div class="link controller"></div>

js

JS-параметры. Если значение не falsy, то миксует и добавляет содержимое в JS-параметры. По умолчанию .

Подробнее про i-bem и JS-параметры.

{
   block: 'link',
   js: true
}

Результат шаблонизации:

<div class="link i-bem" data-bem='{"link":{}}'></div>

Значения будут экранированы:

{
   block: 'link',
   js: { title: 'film "Sid & Nancy"' }
}

Результат шаблонизации:

<div class="link i-bem" data-bem='{"link":{title:"film &#39;Sid &amp; Nancy&#39;"}}'>
</div>

bem

указывает шаблонизатору, нужно ли добавлять классы и JS-параметры для самой БЭМ-сущности и её миксов. По умолчанию .

{
    block: 'menu',
    elem: 'item',
    bem: false
}

Результат шаблонизации:

<div></div>

attrs

хеш HTML-атрибутов. Значения атрибутов .

{
    attrs: {
        id: 'anchor',
        name: 'Мультфильм "Tom & Jerry"'
    }
}

Результат шаблонизации:

<div id="anchor" name="Мультфильм &quot;Tom &amp; Jerry&quot;"></div>

Если любое поле объекта содержит БЭМ-cущность (в данном случае просто объект с полями ///), то для такого объекта шаблонизатор попробует применить шаблоны. Пример:

{
    block: 'button',
    attrs: {
        'data-text': {
            block: 'i-bem',
            elem: 'i18n',
            key: 'button-success'
        }
    }
}
block('i-bem').elem('i18n')({
  
  def: (node, ctx) => node.I18N.get(ctx.key)
});

Результат шаблонизации:

<div id="button" data-text="Поздравляем!"></div>

cls

HTML-класс или классы (через пробел), не принадлежащие к предметной области БЭМ. Например, применение микроформатов или семантической разметки из schema.org.

{
    block: 'user',
    cls: 'h-card p-name',
    content: 'Андрей Линде'
}

Результат шаблонизации:

<div class="user h-card p-name">Андрей Линде</div>

tag

HTML-тег. или укажет движку BEMHTML пропустить этап генерации HTML-тега. По умолчанию .

{
    tag: false,
    content: 'start'
}

Результат шаблонизации:

start

Пользовательские поля

Вы можете добавлять любые другие поля с данными, чтобы потом обработать их в теле шаблона по своему усмотрению.

{
    block: 'link'
    url: '/',
    target: '_blank'
}

Подробнее о том, как обрабатывать пользовательские поля BEMJSON, читайте в разделе про шаблоны.

Читать далее: синтаксис шаблонов

Общие сведения

БЭМ-методология и JavaScript

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

Работа веб-интерфейса обеспечивается несколькими технологиями (HTML, CSS, JS и т.д.). Его реализация разбита на компоненты по блокам. Блок содержит набор файлов технологий, составляющих аспекты его реализации:

  •  — внешний вид блока;

  •  — шаблоны для генерации HTML-представления блока;

  •  — динамическое поведение блока в браузере.

Фреймворк позволяет разложить клиентский JavaScript на компоненты в терминах БЭМ:

  • Блок — JS-компонент, описывающий логику работы однотипных элементов интерфейса. Например, все кнопки могут быть реализованы в виде блока . В этом случае, определяет внешний вид всех кнопок, а  — логику их работы. На каждой странице может размещаться более одного экземпляра блока (например, кнопки). Каждому экземпляру блока соответствует JS-объект, в памяти браузера, хранящий его состояние. JS-объект содержит ссылку на DOM-узел, к которому привязан данный экземпляр блока.

  • Элементы — DOM-узлы, вложенные в DOM-узел блока, с атрибутом , указывающим на их роль в БЭМ-предметной области (имя блока и элемента). Элементы блока доступны через экземпляра блока.

  • Модификаторы — предоставляют информацию о состоянии блока и его элементов. Состояние модификаторов записывается в атрибуте на DOM-узлах блока и элементов. Управление модификаторами производится через экземпляра блока.

Сборка

Разработка в рамках БЭМ-методологии ведется модульно — каждый блок программируется отдельно. Финальный исходный код веб-страниц формируется из кода отдельных блоков с помощью процедур сборки.

В файловой системе блок удобно представлять в виде каталога, а реализацию блока в каждой из технологий — в виде отдельного файла:

    desktop.blocks/
        my-block/
            my-block.css
            my-block.js
            my-block.bemhtml
            ...

    desktop.blocks/
        other-block/
            other-block.css
            other-block.js
            other-block.bemhtml
            ...

Для каждой веб-страницы код использованных на ней блоков может быть собран в единые файлы:

    desktop.bundles/
        index/
            index.html
            index.css
            index.js
            ...

Существует два инструмента, поддерживающих БЭМ-предметную область, для сборки кода результирующих веб-страниц из отдельных описаний блоков:

Оба инструмента позволяют автоматизировать создание HTML-разметки для и .

Почему i-bem.js так называется

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

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

Как использовать i-bem.js

Фреймворк входит в состав библиотеки bem-core.

Реализация состоит из двух модулей:

  • Модуль i-bem.

    Предоставляет базовую реализацию JS-блока , от которой наследуются все блоки и элементы в . Блок написан с расчетом на использование в любом JS-окружении: как на клиенте, так и на сервере (например, в Node.js).

  • Модуль i-bem-dom.

    Предоставляет базовую реализацию блока и элемента, привязанных к DOM-узлу. Рассчитан на использование на клиенте, опирается на работу браузеров с DOM. Зависит от .

Зависимости:

  • jQuery (только для модуля ). При использовании отдельная установка jQuery не требуется.

  • Модульная система ym/modules. При использовании ENB с технологией (и производных от нее) эта зависимость удовлетворяется автоматически.

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

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

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

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

Особенности:

  • Один блок — одна директория.

  • Имена блока и его директории совпадают. Например, блок  — директория , блок  — директория .

  • Реализация блока разделяется на отдельные файлы-технологии. Например, , .

  • Директория блока является корневой для поддиректорий соответствующих ему элементов и модификаторов.


  • Имена директорий элементов начинаются с двойного подчеркивания (). Например, , .

  • Имена директорий модификаторов начинаются с одинарного подчеркивания (). Например, , .

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

Пример

search-form/                           # Директория блока search-form

    __input/                           # Поддиректория элемента search-form__input
        search-form__input.css         # Реализация элемента search-form__input
                                       # в технологии CSS
        search-form__input.js          # Реализация элемента search-form__input
                                       # в технологии JavaScript

    __button/                          # Поддиректория элемента search-form__button
        search-form__button.css
        search-form__button.js

    _theme/                            # Поддиректория модификатора
                                       # search-form_theme
        search-form_theme_islands.css  # Реализация блока search-form, имеющего
                                       # модификатор theme со значением islands
                                       # в технологии CSS
        search-form_theme_lite.css     # Реализация блока search-form, имеющего
                                       # модификатор theme со значением lite
                                       # в технологии CSS

    search-form.css                    # Реализация блока search-form
                                       # в технологии CSS
    search-form.js                     # Реализация блока search-form
                                       # в технологии JavaScript

Такая файловая структура позволяет легко поддерживать и повторно использовать код.

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

Элемент

Составная часть блока, которая не может использоваться в отрыве от него.

Особенности:

  • характеризует смысл («что это?» — «пункт»: , «текст»: ), а не состояние («какой, как выглядит?» — «красный»: , «большой»: ).

  • Структура полного имени элемента соответствует схеме: . Имя элемента отделяется от имени блока двумя подчеркиваниями ().

Пример

<form class="search-form">
    
    <input class="search-form__input">

    
    <button class="search-form__button">Найти</button>
</form>

Вложенность

  • Элементы можно вкладывать друг в друга.

  • Допустима любая вложенность элементов.

  • Элемент — всегда часть блока, а не другого элемента. Это означает, что в названии элементов нельзя прописывать иерархию вида .

Пример

<form class="search-form">
    <div class="search-form__content">
        <input class="search-form__input">
        <button class="search-form__button">Найти</button>
    </div>
</form>


<form class="search-form">
    <div class="search-form__content">
        
        <input class="search-form__content__input">

        
        <button class="search-form__content__button">Найти</button>
    </div>
</form>

Имя блока задает пространство имен, которое элементов от блока ().

Блок может иметь вложенную структуру элементов в DOM-дереве:

Пример

<div class="block">
    <div class="block__elem1">
        <div class="block__elem2">
            <div class="block__elem3"></div>
        </div>
    </div>
</div>

Однако эта же структура блока в методологии БЭМ всегда будет представлена плоским списком элементов:

Пример

.block {}
.block__elem1 {}
.block__elem2 {}
.block__elem3 {}

Это позволяет изменять DOM-структуру блока без внесения правок в коде каждого отдельного элемента:

Пример

<div class="block">
    <div class="block__elem1">
        <div class="block__elem2"></div>
    </div>

    <div class="block__elem3"></div>
</div>

Структура блока меняется, а правила для элементов и их названия остаются прежними.

Принадлежность

Элемент — всегда часть блока и не должен использоваться отдельно от него.

Пример


<form class="search-form">
    
    <input class="search-form__input">

    
    <button class="search-form__button">Найти</button>
</form>



<form class="search-form">
</form>


<input class="search-form__input">


<button class="search-form__button">Найти</button>

Необязательность

Элемент — необязательный компонент блока. Не у всех блоков должны быть элементы.

Пример

<div class="search-form">
    
    <input class="input">

    
    <button class="button">Найти</button>
</div>

Создаем блоки

Блоки из готовой библиотеки появились на странице, но они не взаимодействуют. Теперь создадим блок , который будет брать данные из поля ввода и подставлять их в приветствие. Для этого:

  • Создайте директорию с именем в .

  • Разместите в ней :

    •  — описывает поведение блока;

    •  — содержаит шаблоны для генерации HTML-разметки блока;

    •  — содержит стили блока.

Описываем поведение блока

  • Откройте файл .

  • Вставьте код, который описывает реакцию блока на действие пользователя. При нажатии кнопки в текст приветствия будет подставляться имя пользователя, введенное в поле .

    onSetMod: {
        js: {
            inited: function() {
                this._input = this.findChildBlock(Input);
            }
        }
    },
    _onSubmit: function(e) {
        
        
        e.preventDefault();
        this._elem('greeting').domElem.text('Hello ' + this._input.getVal() + '!');
    },
    {
        lazyInit: true,
        onInit: function() {
            
            this._domEvents().on('submit', this.prototype._onSubmit);
        }
    }
    
  • Используйте модульную систему YModules, чтобы представить данный JavaScript-код:

    modules.define('hello', ,
        
        function(provide, bemDom, Input, Button) {
    
        
        provide(bemDom.declBlock('hello', {
            onSetMod: {
                js: {
                    inited: function() {
                        this._input = this.findChildBlock(Input);
                    }
                }
            },
            _onSubmit: function(e) {
                e.preventDefault();
                this._elem('greeting').domElem.text('Hello ' + this._input.getVal() + '!');
            }
        }, {
            lazyInit: true,
            onInit: function() {
                this._domEvents().on('submit', this.prototype._onSubmit);
            }
        }));
    });
    

Создаем шаблон блока

BEMHTML — технология, которая преобразует входные данные из BEMJSON-файла в HTML.

Чтобы создать шаблон:

  • Откройте файл .

  • Напишите BEMHTML-шаблон, в котором укажите, что блок имеет JavaScript-реализацию.

  • Оберните блок в форму с помощью стандартного режима .

    block('hello')(
        js()(true),
        tag()('form')
    );
    

Добавляем стили блоку

  • Отредактируйте файл :

    .hello
    {
        color: green;
        padding: 10%;
    }
    
    .hello__greeting
    {
        margin-bottom: 12px;
    }
    
  • Создайте дополнительные правила для элемента блока . Они понадобятся, чтобы изменить стили блока из библиотеки bem-components.

    .hello__input
    {
        margin-right: 12px;
    }
    
  • Добавьте блоку дополнительные CSS-правила с помощью поля в файле .

    {
        block : 'input',
        mods : { theme : 'islands', size : 'm' },
    
        
        mix : { block : 'hello', elem : 'input' },
    
        name : 'name',
        placeholder : 'User name'
    }
    

Полный код файла .

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

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

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

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

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

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


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

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

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

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

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

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

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

  • Изменять или полностью перекрывать поведение блока с помощью .

BEMHTML-шаблоны

Разместим на странице под шапкой список товаров. Он представлен в BEMJSON-декларации страницы блоком . Декларация содержит данные о товарах: название, картинку, цену и адрес.

{
    block: 'goods',
    goods: [
        {
            title: 'iPhone 7 128Gb',
            image: 'start-with-project-stub__iphone7.png',
            price: '47 000',
            url: '/'
        },
        {
            title: 'Samsung Galaxy A7 32Gb',
            image: 'start-with-project-stub__samsung.png',
            price: '26 000',
            url: '/'
        },
        {
            
        }
    ]
}

Пример кода index.bemjson.js.

Чтобы эти данные превратились в нужную разметку, блок должен быть реализован в технологии BEMHTML. Для корректировки внешнего вида применим CSS-правила. Воспользуемся командой , чтобы создать блок сразу в двух технологиях:

bem create -l desktop.blocks -b goods -T bemhtml.js -T css

В BEMHTML-шаблоне блока нужно написать код, который превратит данные, задекларированные в BEMJSON, в элементы блока. А также, пользуясь режимом , указать, как будет представлен блок и его элементы в HTML-структуре страницы.

Код пример goods.bemhtml.js.

<!DOCTYPE html>
<html class="ua_js_yes">
    <head>...</head>
    <body class="page">
        <div class="page__inner">
            <div class="head">...</div>
            <ul class="goods">
                <li class="goods__item">
                    <h3 class="goods__title">iPhone 7 128Gb</h3>
                    <img class="goods__image" src="start-with-project-stub__iphone7.png"/>
                    <span class="goods__price">259</span>
                </li>
                <li class="goods__item">...</li>
                <li class="goods__item">...</li>
            </ul>
            <script src="index.min.js"></script>
        </div>
    </body>
</html>

Шаблон может создавать не только HTML-элементы блока, но и другие блоки. Например, цену товара можно сделать ссылкой, используя для этого блок из библиотеки bem-components.

Чтобы избежать вложенных селекторов при оформлении этой ссылки стилями, пометим ее как элемент блока .

{
    elem: 'price',
    content: {
        block: 'link',
        mix: ,
        url: item.url,
        content: item.price
    }
}

Пример кода goods.bemhtml.js.

<ul class="goods">
    <li class="goods__item">
        <h3 class="goods__title">iPhone 7 128Gb</h3>

        <img class="goods__image" src="start-with-project-stub__iphone7.png"/>

        <span class="goods__price">
            <a class="link goods__link" href="/">259</a>
        </span>
    </li>
    //...
    <li class="goods__item">...</li>
    <li class="goods__item">...</li>
</ul>

Нужно визуально выделить на странице новые товары. Для этого добавим проверку модификатора в шаблон: пример.

CSS-правила для блока можно скопировать отсюда.

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

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

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

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

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

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

  • Подписки на изменения модификаторов.

  • Непосредственного вызова методов других экземпляров блоков или статических методов класса другого блока.

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

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

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

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

Приложение Hello, World

Представляет собой динамическое приложение, которое выводит слова «Hello, World!» в выходной поток, демонстрируя тем самым, что оно запускается и может выполнять операции ввода/вывода.

Давайте создадим это приложение, а затем расширим его до .

Чтобы создать приложение Hello, World:

  • Склонируйте шаблонный репозиторий bem-express.

    git clone https://github.com/bem/bem-express.git bem-project
    
  • Перейдите в директорию проекта.

    cd bem-project
    
  • Удалите историю версионирования исходного репозитория.

    rm -rf .git
    
  • Инициализируйте собственный Git-репозиторий.

    git init
    
  • Установите зависимости.

    npm install
    
  • Соберите проект и запустите сервер.

    npm run dev
    

    При запуске приложения в терминале выведется сообщение о том, что сервер выполняется на порту 3000:

    .

    На компьютере запустился:

    • Cервер — отвечает за обработку динамических данных.

    • Nodemon — следит за изменениями на файловой структуре и перезапускает сервер.

    • Chokidar — следит за изменениями в директориях *.blocks/ и перестраивает структуру проекта.

    • Livereload — обновляет страницу в браузере.

  • Откройте браузер и введите адрес localhost:3000.

    Должна открыться страница со следующим содержимым:

    Index page content
    footer content
    
  • Откройте файл server/index.js и внесите следующие изменения в код, начинающегося строкой :

    // ...
    app.get('/', function(req, res) {
    +   var hello = 'Hello';
    +   var world = 'World';
        render(req, res, {
            view: 'page-index',
            title: 'Main page',
            meta: {
                description: 'Page description',
                og: {
                    url: 'https://site.com',
                    siteName: 'Site name'
                }
            },
    +       hello: hello,
    +       world: world
        })
    });
    // ...
    
  • Откройте файл common.blocks/page-index/page-index.bemtree.js и замените его содержимое на следующее:

    block('page-index').content()(function() {
        
        var data = this.data;
        
        return data.hello + ', ' + data.world + '!';
    });
    

    После сохранения, сервер автоматически перезапустится, и содержимое страницы изменится на:

    Hello, World!
    footer content
    

Приложение Hello, World готово.


С этим читают