БЕМ (Блок-Елемент-Модифікатор) - методологія web-розробки, а також набір інтерфейсних бібліотек, фреймворків і допоміжних інструментів.
Блок - це незалежний інтерфейсний компонент. Блок може бути простим або складеним (містити інші блоки). При створенні блоку потрібно забезпечувати можливість його використання в будь-якому місці web-сторінки, а також повторення на тій же самій сторінці. Блок повинен включати в себе всю реалізацію, необхідну для подання частини інтерфейсу, яку він висловлює.
Елемент - це складова частина блоку. Елементи контекстно-залежні: вони мають сенс тільки в рамках свого блоку. Елемент - обов'язкова складова блоку, невеликі блоки обходяться без елементів.
Модифікатор - це властивість блоку або елементу, що задає зміни в їх зовнішньому вигляді або поведінці. Модифікатор може бути булевим (наприклад, button_big) або парою ключ-значення (наприклад, menu_type_bullet, menu_type_numbers). У блоку або елементу може бути кілька модифікаторів одночасно.
БЕМ пропонує загальну семантичну модель для всіх технологій, що використовуються у фронтенд розробці (HTML, CSS, JavaScript, шаблони і ін.)
Використовуючи поняття «блок», «елемент» і «модифікатор» можна описати деревоподібну структуру документа. Такий опис називається BEM tree і є семантичним представленням інтерфейсу, абстракцією над DOM tree.
В HTML / CSS блоки, елементи і модифікатори представлені у вигляді CSS-класів, названих відповідно до правил іменування (naming convention). Кілька блоків можуть бути розташовані на одному і тому ж DOM-вузлі, в цьому випадку DOM-вузлу призначається 2 CSS-класу. На одному DOM-вузлі також можуть бути одночасно розташовані блок і елемент іншого блоку.
CSS-клас блоку відповідає імені блоку. Для поділу слів в складних іменах блоків використовується дефіс.
<Div class = "header"> ... </ div>
<Ul class = "menu"> ... </ ul>
<Span class = "button"> ... </ span>
<Div class = "tabbed-pane"> ... </ div>
CSS-клас елемента містить ім'я блоку і ім'я елемента, розділені двома знаками underscore.
<Div class = "header">
<Div class = "header__bottom"> ... </ div>
</ Div>
<Ul class = "menu">
<Li class = "menu__item"> ... </ li>
</ Ul>
<Span class = "button">
<Input class = "button__control"> ... </ input>
</ Span>
<Div class = "tabbed-pane">
<Div class = "tabbed-pane__panel"> ... </ div>
</ Div>
CSS-клас модифікатора містить ім'я блоку і ім'я модифікатора, розділені одним знаком underscore. У тому випадку, якщо модифікатор - це пара ключ-значення, вони теж поділяються знаком underscore. Для модифікатора елемента в CSS-класі зберігаються і ім'я блоку, і ім'я елемента. CSS-клас модифікатора використовується в парі з класом свого блоку (або елемента).
<Div class = "header header_christmas"> ... </ div> <! - Christmas edition of the header ->
<Ul class = "menu">
<Li class = "menu__item menu__item_current"> ... </ li>
</ Ul>
<Span class = "button button_theme_night"> ... </ span>
<Div class = "tabbed-pane tabbed-pane_disabled"> ... </ div>
Альтернативні правила іменування були запропоновані Гаррі Робертсом [1]. Він радить використовувати 2 дефіса для поділу імен блоку і модифікатора.
<Div class = "header header - christmas"> ... </ div> <! - Christmas edition of the header ->
<Ul class = "menu">
<Li class = "menu__item menu__item - current"> ... </ li>
</ Ul>
<Span class = "button button - theme-night"> ... </ span>
<Div class = "tabbed-pane tabbed-pane - disabled"> ... </ div>
Деякі правила іменування рекомендують використовувати префікси. Так, все класи блоків можуть починатися з префікса b-.
<Div class = "b-header"> ... </ div>
<Ul class = "b-menu"> ... </ ul>
<Span class = "b-button"> ... </ span>
<Div class = "b-tabbed-pane"> ... </ div>
Іноді в якості префікса використовують скорочене ім'я проекту. Наприклад, OraanjePool-> op.
<Div class = "op-header"> ... </ div>
<Ul class = "op-menu"> ... </ ul>
<Span class = "op-button"> ... </ span>
<Div class = "op-tabbed-pane"> ... </ div>
У БЕМ JavaScript працює з абстрактної структурою блоків-елементів та модифікаторів, не звертаючись до лежачих за ним DOM-вузлів і їх CSS-класами безпосередньо. Крім того, для ідентифікації DOM-вузлів не використовуються додаткові CSS-класи "спеціально для JavaScript". Для забезпечення такої можливості використовується фреймворк або власний набір хелперів.
Так, якщо кожному блоку з JavaScript-функціональністю відповідає об'єкт, його методи дозволяють:
звертатися до вкладених елементів:
// припустимо, що blockObj вказує на об'єкт блоку <div class = "tabbed-pane">
blockObj.elem ('panel'); // повертає елементи <div class = "tabbed-pane__panel">
працювати з модифікаторами
// припустимо, що blockObj вказує на об'єкт блоку <div class = "tabbed-pane">
blockObj.setMod ('disabled'); // встановлює модифікатор <div class = "tabbed-pane tabbed-pane_disabled">
blockObj.delMod ('disabled'); // видаляє модифікатор
Оскільки модифікатор відображає стан блоку, при призначенні модифікатора блок або елемент повинен бути приведений у відповідний стан. Для зміни зовнішнього вигляду досить призначення CSS-класу модифікатора. У більш складних випадках приведення блоку в потрібний стан вимагає JavaScript-функціональності. Тому у використовуваного JavaScript-фреймворку повинна бути можливість декларувати список дій, відповідний модифікатору.
BlockObj.on ({
active: function () {
// do smth when active
},
disabled: function () {
// do something when disabled
}
});
Найпростіша структура проекту не передбачає вкладеності в директорії блоків:
button /
button.css
button.js
button.tpl
button__control.css
header /
header.css
header.tpl
header_christmas.css
tabbed-pane /
tabbed-pane.css
tabbed-pane.js
tabbed-pane.tpl
У великих проектах або бібліотеках зручно використовувати вкладену файлову структуру блоку, де для елементів і модифікаторів виділяються директорії.
button /
__control /
button__control.css
button.css
button.js
button.tpl
header /
_christmas /
header_christmas.css
header.css
header.tpl
tabbed-pane /
tabbed-pane.css
tabbed-pane.js
tabbed-pane.tpl
|