# Решение распространенных проблем веб-разработки с помощью БЭМ

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

* [Как упростить код и облегчить рефакторинг](#Как-упростить-код-и-облегчить-рефакторинг)
* [Как получить самодокументируемый код](#Как-получить-самодокументируемый-код)
* [Как начать повторно использовать код и избежать взаимного влияния компонентов друг на друга](#Как-начать-повторно-использовать-код-и-избежать-взаимного-влияния-компонентов-друг-на-друга)
* [Как разместить несколько сущностей на одном DOM-узле и избежать «Copy-Paste»](#Как-разместить-несколько-сущностей-на-одном-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-код страницы, чтобы определить все зависимости. Cелектор всегда содержит знания о том, на какой блок или элемент влияют его правила (в данном случае на элемент `nav__item`). Разработчику не придется думать о возможном существовании кнопки `<div class="button active">Нажми меня!</div>`, так как еe 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#Микс).
