БЭМ — это методология эффективной разработки веб-приложений. Большое количество информации размещено на официальном сайте http://ru.bem.info.
Bemer — шаблонизатор, стремящийся идти по пути упрощения работы с БЭМ. Он должен быть очень удобным для разработки малых и средних проектов.
Исходный код шаблонизатора разделён на подробно задокументированные модули с помощью definer.
Рекомендуется использовать bemer совместно с i-bem.
Bemer доступен в Bower.
bower install bemer
Bemer доступен в NPM.
npm install bemer
<script src="bower_components/bemer/bemer.min.js"></script>var bemer = require('bemer');Общепринятым форматом входящих данных для БЭМ-шаблонизаторов является BEMJSON.
На выходе bemer возвращает простую HTML-строку, готовую для отображения в браузере.
Единственная переменная, предоставляемая шаблонизатором — bemer и она является функцией, которая принимает на вход один параметр — объект в формате BEMJSON.
bemer({ block: 'page' });Результат:
<div class="page"></div>bemer({
block: 'item',
content: [
{ elem: 'title', content: 'Фотоаппарат' },
{ elem: 'price', content: '14999' }
]
});Результат:
<div class="item">
<div class="item__title">Фотоаппарат</div>
<div class="item__price">14999</div>
</div>- Методы
- Функции-помощники
Предназначен для добавления шаблона на одну или несколько БЭМ-сущностей.
В качестве параметров принимает неограниченное количество селекторов на БЭМ-сущности и последним параметром простой объект с полями шаблона.
Возвращает: {bemer}
bemer.match('block1', 'block2', 'blockN', {});По умолчанию для отделения блока от элемента используется двойное подчёркивание: block__element.
А для отделения модификаторов одиночное подчёркивание: block_mod_val.
Блок header:
header
Элемент logo блока header:
header__logo
Блок header с булевым модификатором adaptive:
header_adaptive
Блок header с модификатором theme в значении red:
header_theme_red
Элемент logo блока header с модификатором theme в значении blue:
header_theme_blue__logo
Элемент logo с модификатором size в значении s:
header__logo_size_s
Модификатор у блока и у элемента одновременно:
header_adaptive__logo_size_m
На месте любой части селектора можно записать *, что будет означать произвольное значение.
Любой блок:
*
Любой элемент блока footer:
footer__*
Любое значение модификатора theme блока footer:
footer_theme_*
Любое значение модификатора size любого элемента блока footer:
footer__*_size_*
Поля шаблона содержат информацию по шаблонизации. В основе технической реализации лежит помощник создания классов inherit. Шаблон может иметь произвольный набор полей.
Тип: {function}
Поле construct устанавливает конструктор, который вызывается в начале шаблонизации БЭМ-сущности.
Конструктор принимает два параметра:
{object}bemjson— BEMJSON БЭМ-сущности{object}data— данные о БЭМ-сущности в дереве{number}data.index— индекс сущности среди сестринских элементов{number}data.length— количество сестринских элементов, включая текущий{object}[data.context]— информация о контексте родительского блока{string}[data.context.block]— имя родительского блока{object}[data.context.mods]— модификаторы родительского блока{string}[data.context.elem]— имя родительского элемента{object}[data.context.elemMods]— модификаторы родительского элемента
Получение данных о блоке menu из параметров конструктора:
bemer.match('menu', { construct: function(bemjson, data) {
console.log('bemjson:', bemjson);
console.log('data:', data);
}});
bemer({ block: 'menu' });Вывод:
bemjson: { block: 'menu' }
data: { index: 0, length: 1 }Кроме того, в this существуют поля bemjson и data, аналогичные параметрам конструктора.
Получение данных об элементе item с булевым модификатором active из полей контекста:
bemer.match('menu__item_active', { construct: function(bemjson, data) {
console.log('bemjson:', this.bemjson);
console.log('data:', this.data);
}});
bemer({ block: 'menu', content: [
{ elem: 'item' },
{ elem: 'item', elemMods: { active: true }},
{ elem: 'item' }
] });Вывод:
bemjson: {
block: 'menu',
mods: {},
elem: 'item',
elemMods: { active: true }
}
data: {
index: 1,
length: 3,
context: {
block: 'menu',
mods: {}
}
}Тип: {string} {boolean}
По умолчанию: div
Поле tag устанавливает имя тега.
Блок в представлении тега span:
bemer.match('text', { tag: 'span' });
bemer({ block: 'text' });<span class="text"></span>Для указания стандартного тега div может быть использовано значение true.
Для отмены строкового представления тега используется значение false.
При этом вместо текущего тега будет представлено его содержимое:
bemer.match('anonymous', { tag: false });
bemer({ block: 'anonymous', content: { block: 'inner' }});<div class="inner"></div>Тип: {boolean}
По умолчанию: true для стандартных одиночных тегов
и false для всех остальных
Поле single устанавливает одиночный или парный вид тега.
Блок в представлении частного одиночного тега mytag:
bemer.match('my', { tag: 'mytag', single: true });
bemer({ block: 'my' });<mytag class="my">Тип: {object}
По умолчанию: {}
Поле attrs задаёт атрибуты тега.
Блок текстового поля:
bemer.match('input', { tag: 'input', attrs: { type: 'text' }});
bemer({ block: 'input' });<input class="input" type="text">Атрибуты в шаблоне и входящем BEMJSON складываются:
bemer.match('input', { tag: 'input', attrs: { type: 'text' }});
bemer({ block: 'input', attrs: { placeholder: 'login' }});<input class="input" type="text" placeholder="login">Особый атрибут style
Помимо обычной строки, атрибут style способен принять список CSS-стилей в виде объекта.
Составные имена CSS-свойств допускается указывать как через минус (text-align),
так и в верблюжьей нотации (textAlign).
Если в значении свойства указано число (кроме нуля), ему добавляется единица измерения пикселя (px).
Список CSS-свойств в виде объекта:
bemer.match('text', { attrs: { style: {
width: 100,
height: 0,
'text-align': 'center',
verticalAlign: 'top'
}}});
bemer({ block: 'text' });<div class="text" style="width:100px;height:0;text-align:center;vertical-align:top;"></div>Тип: {boolean} {object}
По умолчанию: false
Поле js указывает на наличие клиентского JavaScript у блока или элемента.
При этом в результирующий HTML-тег добавляется:
- дополнительный CSS-класс, по умолчанию:
i-bem - атрибут с параметрами инициализации, по умолчанию:
data-bem
Блок с JS-реализацией:
bemer.match('menu', { js: true });
bemer({ block: 'menu' });<div class="menu i-bem" data-bem="{"menu":{}}"></div>Элемент с JS-реализацией:
bemer.match('menu__item', { js: true });
bemer({ block: 'menu', elem: 'item' });<div class="menu__item i-bem" data-bem="{"menu__item":{}}"></div>Для передачи инициализационных параметров значением может быть простой объект.
Блок с параметрами инициализации:
bemer.match('man', { js: { name: 'Steve', year: 1955 }});
bemer({ block: 'man' });<div class="man i-bem" data-bem="{"man":{"name":"Steve","year":1955}}"></div>Тип: {boolean}
По умолчанию: true
Поле bem указывает на необходимость добавления сформированных CSS-классов для БЭМ-сущности.
Тег html без БЭМ-классов:
bemer.match('page', { tag: 'html', bem: false });
bemer({ block: 'page' });<html></html>Тип: {string}
По умолчанию: ''
Поле cls позволяет определить произвольную строку, добавляемую в значение атрибута class помимо автоматически генерируемых классов.
Добавление CSS-классов custom1 и custom2:
bemer.match('untypical', { cls: 'custom1 custom2' });
bemer({ block: 'untypical' });<div class="custom1 custom2 untypical"></div>Тип: {object}
По умолчанию: {}
Поле mods задаёт модификаторы блока.
Блок header с модификатором theme в значении red:
bemer.match('header', { mods: { theme: 'red' }});
bemer({ block: 'header' });<div class="header header_theme_red"></div>Модификаторы в шаблоне и входящем BEMJSON складываются:
bemer.match('header', { mods: { theme: 'red' }});
bemer({ block: 'header', mods: { adaptive: true }});<div class="header header_theme_red header_adaptive"></div>Элемент logo блока header с модификатором theme в значении blue:
bemer.match('header__logo', { mods: { theme: 'blue' }});
bemer({ block: 'header', elem: 'logo' });<div class="header__logo header_theme_blue__logo"></div>Особые значения модификаторов
Помимо строк значением модификатора может быть null, true или число.
Значения undefined и false отменяют установку модификатора.
Пример различных типов значений модификаторов:
bemer.match('example', { mods: {
string: 'text',
nil: null,
yes: true,
zero: 0,
number: 5,
undef: undefined,
no: false
}});
bemer({ block: 'example' });<div class="example example_string_text example_nil_null example_yes example_zero_0 example_number_5"></div>Изменение списка модификаторов в шаблоне
При изменении списка модификаторов автоматически накладываются подходящие шаблоны:
bemer
.match('button', { mods: { theme: 'normal' }})
.match('button_theme_normal', { tag: 'span' });
bemer({ block: 'button' });<span class="button button_theme_normal"></span>Тип: {object}
По умолчанию: {}
Поле elemMods задаёт модификаторы элемента.
Модификаторы в шаблоне и входящем BEMJSON складываются.
Элемент logo с модификатором size в значении s:
bemer.match('header__logo', { elemMods: { size: 's' }});
bemer({ block: 'header', elem: 'logo' });<div class="header__logo header__logo_size_s"></div>При изменении списка модификаторов автоматически накладываются подходящие шаблоны:
bemer
.match('button__label', { elemMods: { size: 'm' }})
.match('button__label_size_m', { tag: 'label' });
bemer({ block: 'button', elem: 'label' });<label class="button__label button__label_size_m"></label>Тип: {array}
По умолчанию: []
Поле mix задаёт список БЭМ-сущностей, которые необходимо примешать к текущей сущности.
В результате примешивания добавляются CSS-классы и JS-параметры.
Примешивание блока clearfix к блоку header:
bemer.match('header', { mix: [{ block: 'clearfix' }] });
bemer({ block: 'header' });<div class="header clearfix"></div>Примешивание элемента menu с JS-параметрами:
bemer.match('header', { mix: [{ elem: 'menu', js: { length: 10 }}] });
bemer({ block: 'header' });<div class="header i-bem header__menu" data-bem="{"header__menu":{"length":10}}"></div>Примешиваемые сущности в шаблоне и входящем BEMJSON складываются:
bemer.match('header', { mix: [{ block: 'clearfix' }] });
bemer({ block: 'header', mix: [{ block: 'menu', elem: 'wrap' }] });<div class="header menu__wrap clearfix"></div>Тип: {*}
Поле content задаёт содержимое HTML-элемента.
Значением может быть произвольный тип данных, в том числе BEMJSON любого уровня вложенности.
Простая строка в содержимом блока page:
bemer.match('page', { content: 'Hello world!' });
bemer({ block: 'page' });<div class="page">Hello world!</div>Добавление блока header и элемента footer в содержимое блока page:
bemer.match('page', { content: [
{ block: 'header' },
{ elem: 'footer' }
] });
bemer({ block: 'page' });<div class="page">
<div class="header"></div>
<div class="page__footer"></div>
</div>Тип: {object}
Поле options устанавливает опции выполнения шаблона,
с помощью которых можно переопределить глобальные настройки
только для заданных сущностей.
Принимаемые параметры:
{boolean|object}[escape]— флаг экранирования спецсимволов{boolean}[escape.content]— флаг экранирования содержимого{boolean}[escape.attrs]— флаг экранирования значений атрибутов
В значении поля шаблона можно записывать функцию.
Функция, указанная в значении стандартного поля шаблона принимает параметром значение одноимённого поля из BEMJSON и должна возвращать свой корректный тип данных.
Функция поля tag должна возвращать строку:
bemer.match('bold', { tag: function(bemjsonTag) {
if(bemjsonTag === 'span') {
return 'b';
}
}});
var any = bemer({ block: 'bold', tag: 'span' });В результате у блока bold будет тег b:
<b class="bold"></b>Кроме параметра функции в стандартных полях шаблона, для получения значений из BEMJSON можно использовать объект this.bemjson.
Функция поля content может возвращать произвольный тип данных:
bemer.match('header', { content: function() {
return this.bemjson.content || 'Hello world!';
}});
bemer({ block: 'header' });В содержимое блока header было установлено значение по умолчанию, так как в BEMJSON оно не было указано:
<div class="header">Hello world!</div>При одновременном указании примитивного типа в значении стандартного поля шаблона и в BEMJSON, приоритет отдаётся значению из BEMJSON.
Одновременное указание строки в значении поля BEMJSON и шаблона:
bemer.match('text', { tag: 'span' });
bemer({ block: 'text', tag: 'b' });Приоритет у BEMJSON, поэтому блоку text будет присвоен тег b:
<b class="text"></b>При одновременном указании массива или объекта в значении стандартного поля шаблона и BEMJSON, значения будут складываться с приоритетом у BEMJSON.
Одновременное указание объекта атрибутов в значении поля BEMJSON и шаблона:
bemer.match('input', { attrs: {
type: 'text',
name: 'age'
}});
var header = bemer({ block: 'input', attrs: { type: 'number' }});Приоритет у BEMJSON, поэтому атрибуту type блока input будет присвоено значение number:
<div class="input" type="number" name="age"></div>В качестве значения стандартному полю шаблона можно указать функцию. При этом вероятно, что значение возвращается на основе нелинейной логики, и возможно с использованием значения из BEMJSON. Таким образом, у значения, возвращаемого функцией приоритет по отношению к BEMJSON.
Указание тега в функции шаблона и в BEMJSON одновременно:
bemer.match('text', { tag: function() { return 'span'; }});
bemer({ block: 'text', tag: 'b' });Приоритет у функции, поэтому блоку text будет присвоен тег span:
<span class="text"></span>Кроме стандартных полей можно задавать произвольные поля шаблона.
Они будут доступны в this.
Произвольное поле sum складывает два числа:
bemer.match('sum', {
content: function() {
return this.sum(this.bemjson.a, this.bemjson.b);
},
sum: function(a, b) {
return a + b;
}
});
bemer({ block: 'sum', a: 3, b: 7 });Сумма устанавливается в содержимое:
<div class="sum">10</div>По мере декларации поступающие шаблоны могут быть унаследованы от добавленных ранее. Шаблонизатор делает это самостоятельно.
Декларация нескольких шаблонов на блок input:
bemer
.match('input', { js: true, tag: 'input' })
.match('input', { attrs: { type: 'text' }})
.match('input_inactive', { js: false });
bemer({ block: 'input', mods: { inactive: true }});Результат совмещает в себе все указанные правила:
<input class="input input_inactive" type="text">Наследование производится только от шаблонов с более общими селекторами. Например, шаблон на блок с модификатором будет унаследован от шаблона на одноимённый блок, но не наоборот. Аналогичные правила от общего к частному действуют для шаблонов со звёздочками в селекторах.
Декларация элементов блока header:
bemer
.match('header__*', { tag: 'span' })
.match('header__logo', { attrs: { title: 'logo' }})
.match('header__logo_theme_*', { mix: [{ block: 'coloring' }] })
.match('header__logo_theme_red', { content: '#fff' });
bemer({ block: 'header', elem: 'logo', elemMods: { theme: 'red' }});Все шаблоны задекларированы в порядке от общего к частному, поэтому результат совмещает в себе все указанные правила:
<span class="header__logo header__logo_theme_red coloring" title="logo">#fff</span>При наследовании шаблонов есть возможность получить предыдущее значение поля.
Блок text формирует своё содержимое по цепочке шаблонов:
bemer
.match('text', { content: 'Hello' })
.match('text', { content: function() { return this.__base() + ' world'; }})
.match('text', { content: function() { return this.__base() + '!'; }});
bemer({ block: 'text' });В результате выполнения цепочки вызовов в содержимое устанавливается строка «Hello world!»:
<div class="text">Hello world!</div>Устанавливает настройки шаблонизации в соответствии с переданными параметрами или сбрасывает настройки до стандартных при вызове без параметров.
Метод принимает один необязательный параметр:
{object}[config]— конфигурационные параметры{object}[config.delimiters]— разделители имён{string}[config.delimiters.mod=_]— разделитель блока и модификатора, элемента и модификатора, модификатора и значения{string}[config.delimiters.elem=__]— разделитель блока и элемента
{boolean|object}[config.xhtml=false]— флаг формирования тегов в формате XHTML{boolean}[config.xhtml.repeatBooleanAttr=false]— флаг автоповтора булева атрибута{boolean}[config.xhtml.closeSingleTag=false]— флаг закрытия одиночного тега
{boolean|object}[config.escape=true]— флаг экранирования спецсимволов{boolean}[config.escape.content=true]— флаг экранирования содержимого{boolean}[config.escape.attrs=true]— флаг экранирования значений атрибутов
{string}[config.tag=div]— стандартное имя тега{string}[config.bemClass=i-bem]— имя класса для js-инициализации{string}[config.bemAttr=data-bem]— имя атрибута для хранения параметров инициализации{string}[config.idPrefix=i]— префикс идентификаторов, формируемых помощником id
Возвращает: {bemer}
Изменение разделителей и стандартного имени тега:
bemer
.config({
delimiters: {
mod: '=',
elem: '--'
},
tag: 'span'
})
.match('header--logo=size=s', { content: 'logo' });
bemer({ block: 'header', elem: 'logo', elemMods: { size: 's' }});Результат работы шаблонизатора с изменёнными настройками:
<span class="header--logo header--logo=size=s">logo</span>В продолжение предыдущего примера, если вызвать метод config без параметров, то все настройки сбросятся до стандартных и селекторы последующих шаблонов нужно будет записывать со стандартными разделителями:
bemer
.config()
.match('header__logo_size_s', { attrs: { title: 'logo' }});
bemer({ block: 'header', elem: 'logo', elemMods: { size: 's' }});Результат работы шаблонизатора со сброшенными до стандартных настройками:
<span class="header__logo header__logo_size_s" title="logo">logo</span>Удаляет все задекларированные шаблоны и сбрасывает порядковый номер для формирования идентификаторов помощником id.
Возвращает: {bemer}
Удаление шаблона на блок name:
bemer
.match('name', { tag: 'span' })
.clean();
bemer({ block: 'name' })Блоку name будет установлен тег div, потому что шаблон был удалён:
<div class="name"></div>Шаблонизатор bemer состоит из самостоятельных модулей, каждый из которых предназначен для выполнения собственных целей.
Имеющиеся модули достаточно абстрактные, благодаря чему могут быть использованы за пределами bemer:
Tag— работа с тегомSelector— работа с БЭМ-селекторомNode— работа с БЭМ-узломMatch— проверка БЭМ-узла на соответствие шаблону
Для получения вышеперечисленных модулей предназначен метод modules, который принимает один необязательный параметр:
{string}[name]— имя модуля
Возвращает: {object|*} — все модули или один заданный модуль
Использование модуля Selector:
var Selector = bemer.modules('Selector');
var header = new Selector('header_theme')
.modVal('dark')
.elem('logo')
.elemMod('size', 's');
console.log(header.toString());В консоль будет выведена строка:
header_theme_dark__logo_size_s
В bemer существуют функции-помощники.
Это такие функции, которые доступны в this из всех шаблонов.
Помимо стандартных помощников можно добавлять свои собственные с помощью метода helper.
Метод принимает два обязательных параметра:
{string}name— имя помощника{function}callback— тело функции помощника
Возвращает: {bemer}
Добавление и использование помощника multi, который умножает два числа:
bemer
.helper('multi', function(a, b) {
return a * b;
})
.match('header', {
content: function(content) {
return this.multi(content[0], content[1]);
}
});
bemer({ block: 'header', content: [4, 9] })Произведение устанавливается в содержимое:
<div class="header">36</div>Функции-помощники доступны в this из всех шаблонов.
Помимо стандартных помощников можно добавлять свои собственные с помощью метода helper.
Проверяет на первый элемент среди сестринских.
Возвращает: {boolean}
Проверяет на последний элемент среди сестринских.
Возвращает: {boolean}
Проверяет БЭМ-сущность на блок.
Возвращает: {boolean}
Проверяет БЭМ-сущность на элемент.
Возвращает: {boolean}
Формирует уникальное значение для HTML-атрибута id.
Результат состоит из префикса, соли в виде случайного четырёхзначного числа и итерируемого числа начиная с нуля: i + 6140 + 0.
Соль необходима для сохранения уникальности идентификаторов при шаблонизации одновременно на клиенте и сервере.
Для абсолютной гарантии уникальности рекомендуется изменить префикс на клиенте или сервере.
Изменить префикс глобально можно с помощью метода config.
Изменить префикс для конкретного формируемого идентификатора можно через параметр помощника.
Параметры:
{string}[prefix=i]— префикс для формируемого идентификатора, по умолчаниюi
Возвращает: {string}
Экранирует строку текста.
Предваряет дополнительным слешем: слеш, кавычки, символы перевода строки, каретки и табуляции.
Параметры:
{string}string— строка
Возвращает: {string}
Деэкранирует строку текста.
Параметры:
{string}string— строка
Возвращает: {string}
Экранирует HTML-строку.
Заменяет на HTML-сущности: амперсанд, угловые скобки и кавычки.
Содержимое экранируется автоматически для любой БЭМ-сущности.
Параметры:
{string}string— строка
Возвращает: {string}
Деэкранирование HTML-строки.
Параметры:
{string}string— строка
Возвращает: {string}
Удаляет повторяющиеся пробелы.
Параметры:
{string}string— строка
Возвращает: {string}
Вырезает HTML-теги.
Параметры:
{string}string— строка
Возвращает: {string}
Переводит всю строку, заданный символ или промежуток символов в верхний регистр.
Параметры:
{string}string— строка{number}[indexA]— порядковый номер символа{number}[indexB]— порядковый номер символа для указания промежутка
Возвращает: {string}
Переводит всю строку, заданный символ или промежуток символов в нижний регистр.
Параметры:
{string}string— строка{number}[indexA]— порядковый номер символа{number}[indexB]— порядковый номер символа для указания промежутка
Возвращает: {string}
Повторяет строку заданное количество раз с указанным разделителем
Параметры:
{string}string— строка{number}n— количество повторений{string}[separator]— разделитель, по умолчанию отсутствует
Возвращает: {string}
Возвращает случайное число.
При вызове без аргументов возвращает случайное дробное число от 0 до 1.
При вызове с указанием минимума и максимума возвращает дробное число из этого промежутка.
При вызове со всеми тремя аргументами возвращает число из заданного промежутка, делящееся без остатка на указанный шаг.
Параметры:
{number}[min]— минимум{number}[max]— максимум{number}[step]— шаг
Возвращает: {number}
Расширяет объект.
Параметры:
{object}object— расширяемый объект{...object}source— расширяющие объекты в любом количестве
Возвращает: {object}
Расширяет объект рекурсивно.
Параметры:
{object}object— расширяемый объект{...object}source— расширяющие объекты в любом количестве
Возвращает: {object}
Клонирует объект.
Параметры:
{object}object— клонируемый объект
Возвращает: {object}
Клонирует объект рекурсивно.
Параметры:
{object}object— клонируемый объект
Возвращает: {object}
Каждый из этих помощников принимает неограниченное количество параметров.
Если все переданные параметры принадлежат типу данных, на который осуществляется проверка, помощники возвращают true, иначе false.
Параметры:
{...*}subject— параметры
Возвращают: {boolean}
Список помощников для проверки на определённый тип данных:
is.string— строкаis.number— числоis.nan— NaNis.boolean— логический типis.null— nullis.undefined— undefinedis.date— датаis.regexp— регулярное выражениеis.array— массивis.map— простой объект (хэш, карта)is.argument— аргументы функцииis.function— функцияis.native— системная функция
Определяет тип переданных параметров.
Возвращает строку, соответствующую имени одного из помощников для проверки на определённый тип данных, которые перечислены выше.
Возвращает mixed, если были переданы параметры разных типов данных.
Параметры:
{...*}subject— параметры
Возвращает: {string}
Проверяют параметры на число определённого вида.
Параметры:
{...*}subject— параметры
Возвращают: {boolean}
Список помощников для проверки чисел:
is.integer— целое числоis.float— дробное число
Проверяет параметры на примитивные типы данных.
В примитивные типы входят: string, number, NaN, boolean, null, undefined.
Параметры:
{...*}subject— параметры
Возвращает: {boolean}
Проверяет параметры на единый тип данных.
Возвращает true, если все переданные параметры относятся к одному типу данных, иначе false.
Параметры:
{...*}subject— параметры
Возвращает: {boolean}