# Рішення типових проблем веб-розробки з допомогою БЕМ

Методологія БЕМ задає [правила іменування CSS селекторів](../naming-convention/naming-convention.ru.md), дотримання яких вирішує ряд проблем веб-розробки і відповідає на наступні питання:

* [Як спростити код і полегшити рефакторинг](#Як-спростити-код-і-полегшити-рефакторинг)
* [Як отримати самодокументируемый код](#Як-отримати-самодокументируемый-код)
* [Як почати повторно використовувати код і уникнути взаємного впливу компонентів один на одного](#Як-почати-повторно-використовувати-код-і-уникнути взаємного впливу-компонентів-один-на-одного)
* [Як розмістити кілька сутностей на одному DOM-вузлі і уникнути «Copy-Paste»](#Як-the-кілька-сутностей-на-одному-dom-сайті-і-уникнути-copy-paste)

## Як спростити код і полегшити рефакторинг

**Проблема**

При верстці проекту компоненти інтерфейсу отримують імена на основі контексту, з яким працює розробник. Зазвичай контекстом служить сторінка або якась її частина.

Коли сторінку верстає один розробник в короткі терміни, колізії імен можна уникнути. Але якщо над проектом працюють кілька осіб або правки потрібно внести через якийсь час, то відстежити залежні імена компонентів стає складно. У великих проектах результатом правки одного класу може стати десяток «роз'їхалися» сторінок.

Наприклад, для створення навігаційного меню можуть використовуватися такі імена класів:

```html
<ul class="nav">
    <li class="item active"><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-правил:

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

.active
{
    font-weight: bold;
    background: #ffc7c7;
}
```

Якщо знадобиться додати на сторінку інший компонент, що містить пункти, стилі для нового `item` вплинуть на пункти з існуючого навігаційного меню.

Або припустимо, що в навігаційному меню потрібно змінити правила класу `.active`. По імені незрозуміло компоненти, які його використовують. Може виявитися, що на іншій сторінці існує, наприклад, кнопка `<div class="button active">Натисни мене!</div>`. Тоді зміна правил для `.active` вплине на стилі цієї кнопки.

Щоб розібратися, чи можна безболісно змінити стилі для класу `.active`, розробнику доведеться переглянути всю структуру сторінки або проекту. Будь-яка зміна потребуватиме значних часових витрат тільки на пошук залежних компонентів.

**Рішення**

Методологія БЕМ вирішує проблему колізії імен за допомогою [угоди щодо іменування CSS-класів](../naming-convention/naming-convention.ru.md#Угода-по-іменування-css селекторів), надаючи всім компонентів та їх складових унікальні імена.

Застосування правил іменування дозволяє:

* задавати унікальні імена [БЕМ-сутностей](../key-concepts/key-concepts.ru.md#БЕМ-сутність);
* відстежувати ієрархічні зв'язки в межах блоку;
* спрощувати сприйняття коду;
* отримувати [самодокументируемый код](#Як-отримати-самодокументируемый-код).

Розглянемо той же приклад навігаційного меню:

```html
<ul class="nav">
    <li class="item active"><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>
```

Але застосуємо до нього правила іменування БЕМ: клас `nav` буде позначати ім'я блоку, `nav__item` і `nav__link` — імена елементів, а `nav__item_active` — ім'я модифікатора елемента `item`.

У такому випадку запис буде наступною:

```html
<ul class="nav">
    <li class="nav__item nav__item_active"><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>
```

І, відповідно, CSS буде мати такий вигляд:

```css
.nav__item
{
    padding: 4px 10px;
    color: black;
}

.nav__item_active
{
    font-weight: bold;
    background: #ffc7c7;
}
```

Нові імена CSS-класів містять всю інформацію про структуру блоку. А це значить, що більше не потрібно переглядати HTML-код сторінки, щоб визначити всі залежності. Селектор завжди містить знання про те, на який блок або елемент впливають його правила (в даному випадку на елемент `nav__item`). Розробникам не доведеться думати про можливе існування кнопки `<div class="button active">Натисни мене!</div>`, так як її CSS-правила будуть записані як `.button_active` і не будуть залежати від правил модифікатора `active` для пункту меню (`nav__item_active`).

>Використання довгих імен має наступні недоліки:

>* Результуючий код важить більше. Ця проблема вирішується `gzip`, який стискає повторювані послідовності в іменах.

>* Часу на написання класів витрачається більше. Цю проблему допомагають вирішити автозаповнення в редакторі, використання CSS препроцесорів і шаблонизаторов, які автоматично додають префікси. Довгі імена класів надають явні зв'язки між складовими частинами компонентів, що економить час на вивчення архітектури проекту.

### Як отримати самодокументируемый код

**Проблема**

При переході на новий проект розробник витрачає багато зусиль на те, щоб зрозуміти, як все влаштовано, де що лежить і як працює код. Часом саме на таке вивчення йде велика частина часу, а зовсім не на реалізацію функціональності або виправлення помилки.

**Рішення**

Одна з цілей БЕМ — дати зрозуміти, що робить той чи інший код, тільки за назвами класів. Ідея самодокументируемого коду полягає в тому, щоб при перегляді CSS-класів, змінних і функцій було зрозуміло, як працює код, і як взаємодіють компоненти інтерфейсу.

Використовуючи БЕМ, можна отримати HTML з іменами класів, показують взаємодію таких частин коду:

* незалежних [блоків](../key-concepts/key-concepts.ru.md#Блок);
* [елементів](../key-concepts/key-concepts.ru.md#Елемент) (дочірніх компонентів) цього блоку;
* [модифікаторів](../key-concepts/key-concepts.ru.md#Модифікатор) блоку або елемента.

Розглянемо приклад з формою пошуку на сайті. Не будемо звертатися до HTML, спробуємо прочитати тільки CSS і зрозуміти, яку частину інтерфейсу він описує.

Варіант реалізації форми в класичній верстання:

```css
form {}

input
{
    background: red;
}

input[type=submit]
{
    background: buttonface
}
```

Такий спосіб запису не відображає зв'язок між:

* компонентами та їх складовими частинами;
* селекторами і конкретними компонентами інтерфейсу, до яких вони відносяться.

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

Напишемо CSS на класи:

```css
.form {}
.field {}
.submit {}
```

Код став інформації: тепер зрозуміло, що є форма, поле і якийсь компонент `submit`. Але такі імена все ще не дають зрозуміти, чи відноситься поле (`field`) до формі (`form`), або що станеться, якщо полів або форм на сторінці буде кілька. Знову виникає необхідність звертатися до HTML.

Перепишемо приклад, використовуючи [угода щодо іменування БЕМ](../naming-convention/naming-convention.ru.md):

```css
.form {}
.form_search {}
.form__field {}
.form__submit-button {}
```

Такий запис дає зрозуміти, як працює даний код. Імена CSS-класів показують, що:

* Існує форма, реалізована блоком `form`.
* Модифікатор `form_search` вказує на те, що мова йде про форму пошуку.
* У форми є складові — вкладені елементи: поле `form__field` кнопка `form__submit-button`.

Дотримання угоди щодо іменування БЕМ дозволяє зрозуміти структуру блоку без докладного вивчення HTML. Навіть при появі на сторінці ще одного поля (крім `form__field`), його правила ніяк не будуть впливати на елементи пошукової форми. Нове поле буде реалізовано як елемент іншого блоку і буде мати своє унікальне ім'я. Наприклад, `attach__field`.

Правила іменування БЕМ дозволяють зробити код проекту однозначним і, як наслідок, інформативним. Це знижує поріг входу для інших розробників.

## Як почати повторно використовувати код і уникнути взаємного впливу компонентів один на одного

**Проблема**

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

Розглянемо проблему на прикладі навігаційного меню:

```html
<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` можуть бути записані як:

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

Якщо на сторінку знадобиться додати додаткові компоненти, що містять пункти, то з'явиться ще один блок коду з класом `item`, наприклад:

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

У цьому випадку CSS може бути оформлений за допомогою каскадів. Для цього достатньо довизначити правила, вже написані для `.item`:

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

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

Такий код може працювати до тих пір, поки не виникне необхідність змінити сторінку. Наприклад, перемістити пункти меню, використовувати написаний код у іншому місці окремо від батьківського компонента або вкласти навігаційне меню в блок `snippets`.

Використання каскадів пов'язує незалежні компоненти інтерфейсу: немає можливості виправити один компонент, не торкнувшись стилі іншого.

**Рішення**

[Правила іменування CSS селекторів](../naming-convention/naming-convention.ru.md) дають можливість вносити зміни точково, не зачіпаючи залежні компоненти. У БЕМ кожен блок має унікальне ім'я і є самодостатнім.

Запишемо той же код згідно з правилами іменування БЕМ:

```html
<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>
```

```css
.nav__item
{
    padding: 4px 10px;
    color: black;
}
```

У такому разі додавання нового пункту `item` на сторінку буде виглядати так:

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

Пункт `snippets__item` буде мати відповідні тільки йому унікальні CSS-правил:

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

Зміни в `nav__item` не впливають на `snippets__item`, так як пункти отримують унікальні імена завдяки [простору імен](https://ru.wikipedia.org/wiki/Пространство_имен_%28программирование%29), заданим ім'ям блоку. Це дозволяє формувати незалежні CSS-правила для всіх елементів блоку.

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

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

### Використання каскадів у БЕМ

Методологія БЕМ допускає використання каскадів.

Наприклад, каскад доречний, щоб змінювати елементи в залежності від стану блоку або заданої йому теми:

```css
.nav_hovered .nav__link
{
    text-decoration: underline;
}
```

```css
.nav_theme_islands .nav__item
{
    line-height: 1.5;
}
```

_______________________________________________

**Важливо!** Застосування каскаду збільшує зв'язаність коду і робить його повторне використання неможливим.
________________________________________________

## Як розмістити кілька сутностей на одному DOM-вузлі і уникнути «Copy-Paste»

**Проблема**

При роботі з проектами може знадобитися повторно використовувати реалізовану функціональність.

У багатьох випадках таку проблему вирішують копіюванням потрібної частини коду в новий компонент. Такий підхід має наступні недоліки:

* збільшується кодова база проекту;
* може налагодження коду при виявленні помилки.

Як наслідок, розробник змушений підтримувати більшу кількість рядків коду, а виправлення треба вносити окремо в кожну реалізацію. Це збільшує часові витрати на налагодження і підтримку проекту.

**Рішення**

Скористаємося прикладом, який реалізує універсальний блок навігаційного меню та написаний за всіма [правилами іменування БЕМ](../naming-convention/naming-convention.ru.md).

```html
<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>
```
Такий блок можна використовувати, наприклад, для навігації по статтям в блоці новин.

Припустимо, в розділі новин вже є блок `articles`, якому написані всі необхідні CSS-правила.

Змішати реалізації двох різних блоків без копіювання коду можна за допомогою [міксу](../key-concepts/key-concepts.ru.md#Мікс). Тобто розмістити на одному DOM-сайті блок `nav` елемент `articles__nav`.

У коді це буде виглядати так:

```html
<ul class="nav articles__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` і особливості реалізації елемента `articles__nav` (зовнішній вигляд новинних статей в меню). При цьому немає необхідності копіювати вже наявні CSS-правила. При виявленні помилки, виправлення необхідно буде внести тільки одну частину коду.

>Міксувати можна не тільки блоки з елементами, але і інші БЕМ-сутності. Детальніше про варіанти використання міксів в БЕМ читайте у розділі [Основні поняття БЕМ](../key-concepts/key-concepts.ru.md#Мікс).
