bem-site / bem-forum-content-ru

Content BEM forum for Russian speak users
MIT License
56 stars 6 forks source link

Вкладывание элементов в элементы и другие тонкости #158

Open tadatuta opened 9 years ago

tadatuta commented 9 years ago

По мотивам обсуждения в твиттере с подачи @delaz, решили написать пост и собрать еще вопросов и тем для разъяснения, которые в будущем планируем добавить в раздел про методологию.

Что у нас получилось?

  1. БЭМ — это не про префиксы.
  2. БЭМ — это не про длинные названия классов.
  3. Нельзя использовать элементы элементов в нейминге.
  4. Миксы.
  5. БЭМ не запрещает каскад (но и не приветствует).
  6. Как понять, когда делать блок, а когда — элемент.

Пойдем по порядку, а вы задавайте вопросы или дополняйте список в комментариях. Ответы будем выносить в пост.

«БЭМ — это длинные имена классов. b-button — это длинно!»

Отвечаем: БЭМ не навязывает префиксы. Чтобы писать хорошо поддерживаемый и реиспользуемый код, префиксы совершенно не нужны. Исторически они появились в переходный период для того, чтобы отличать новый код, написаный по БЭМ, от старого. Со временем мы от них отказались. Если посмотреть в код, можно увидеть, что префиксов там нет.

«А как же префикс js- или i- для блоков без визуального представления?»

Когда-то можно было сверстать сайт практически без JS-кода. Сейчас большая часть блоков имеет JS-представление. Глядя на текущую ситуацию мы поняли, что нет нужды как-то отличать технологии реализации блока на уровне нейминга. Посмотрите, к примеру, на Web Components. Объективно необходимости в префиксах нет, но каждый волен выбирать, что ему, проекту или команде удобнее и/или привычнее.

«Префиксы — ладно. А просто длинные названия блоков?»

В этом и похожих случаях вы можете использовать классы типа btn вместо button. Это никак не помешает вам разрабатывать по БЭМ. Все как с названием переменных в JS: context, ctx или c. Но помните, вам, вашим коллегам и тем, кто будет разрабатывать и поддерживать проект после вас это предстоит читать, понимать и с этим работать ;)

Остается вопрос про неймспейсы — мы пишем имя блока перед именем элементов (button__icon) и модификаторов (button_active).

Если декомпозировать «проблему», можно выделить 2 потенциальных минуса:

И тогда, благодаря неймспейсам, будет решено следующее:

  1. Исчезнет опасность случайно «задеть» внутреннее устройство блока. Мы получим аналог скоупа в JS, но для CSS. Для этого в Web Components придумали Shadow DOM, но в действительности простого добавления имени блока достаточно, чтобы получить тот же результат без лишних телодвижений.
  2. С первого взгляда на любой класс, хоть в CSS, хоть в HTML, хоть в любой другой технологии реализации блока, мы тут же поймем, к какому блоку он относится и какую задачу решает. Сравните: active (на что повлияет этот класс?) VS. input_active или item VS. nav__item.

    «Что вы скажете на nav__item__link? Все равно длинно»

Да. И к тому же не по БЭМу :)

Неймспейсом служит только имя блока. А отражать вложенность в именах элементов не нужно. Это не только длинно, но еще и не позволит при повторном использовании блока в другой ситуации (или просто при рефакторинге) легко вынуть один элемент из другого. Плоская структура касается не только нейминга, но и расположения кода на файловой системе:

nav/
    __item/
        nav__item.css
    __link/
        nav__link.css

Для выражения вложенности вполне достаточно DOM-дерева:

<ul class="nav">
    <li class="nav__item">
        <a class="nav__link"></a>
    </li>
</ul>

«Логично. Как отличить — делать блок или элемент? Например, nav__link — это элемент меню или самостоятельный блок link, который будет использоваться и в других местах на странице?»

Тут нам на помощь приходят миксы — возможность смешать на одном DOM-узле несколько блоков (или элементов/модификаторов) одновременно.

Предыдущий пример вполне может выглядеть так:

<ul class="nav">
    <li class="nav__item">
        <a class="link nav__link"></a>
    </li>
</ul>

При этом все общее, что есть у всех ссылок на проекте, будет описано в блоке link, а особенности, присущие только ссылке внутри nav — для nav__link.

«Почему нельзя стилизовать ссылки каскадом через .nav__item .link

  1. Это несемантично. А если там в будущем окажется не ссылка вовсе?
  2. Во-вторых, это влечет за собой дополнительные сложности, ведь каскад затронет и вложенные сущности. Например, если в будущем вы захотите усложнить архитектуру или добавить выпадающее меню.
  3. Наконец, структура может поменяться и nav__item совсем исчезнуть.

    «Получается, что в каскадных таблицах стилей нельзя использовать каскад?»

Можно. Но нужно понимать, какие последствия это влечет. Например, каскад уместен, чтобы менять элементы в зависимости от состояния блока (.nav_hover .nav__link { text-decoration: underline; }) или, скажем, темы (.nav_theme_islands .nav__item { line-height: 1.5; }).

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

«Как в принципе отличать, где блок, а где элемент?»

Если хочется переиспользовать кусок кода вне контекста родителя — это точно блок. Если кусок кода не имеет смысла без родителя — это скорее всего элемент. Аналогией служит Shadow DOM в Web Components.

Исключением может быть ситуация, когда у такого элемента оказывается слишком богатый внутренний мир и возникает желание сделать его собственные элементы. Тогда это можно представить как служебный «приватный» блок.

Что скажете? Наши ответы помогут вам разобраться в ваших ситуациях или же нам стоит подумать над чем-то еще? Будет очень здорово получить от вас вопросы или же примеры, которые мы сможем объяснить. А потом вынести всю эту полезную информацию в раздел сайта.

Очень ждем ваших комментариев!

varya commented 9 years ago

Даже не знаю, напоминает ли этот текст мне что-то или нет...

voischev commented 9 years ago

Я подробнее хотел бы почитать про тему «БЭМ — это не только про CSS» :)

hudochenkov commented 9 years ago

Тогда это можно представить как служебный «приватный» блок.

Можно уточнить что это за «приватный» блок и как это выглядит в коде?

tadatuta commented 9 years ago

Можно уточнить что это за «приватный» блок и как это выглядит в коде?

Принципиально отличий нет. Примером такого блока в bem-components является menu-item — он не имеет смысла без menu, но сам по себе достаточно сложный, чтобы вынести его в отдельный блок. При желании можно ввести какой-нибудь префикс, чтобы отличать такие блоки.

tadatuta commented 9 years ago

@voischev и про это напишем обязательно

mishanga commented 9 years ago

Опечатка в примере про каскад: .nav__hover => .nav_hover

mishanga commented 9 years ago

Можно еще написать, что: 1) элементы не должны болтаться в дереве на пределами родителя (когда, например, к блоку миксуются чужие элементы) 2) не должно быть классов, где блок указан только с модификатором (пример: https://github.com/crushlovely/skyline/blob/master/example-forms.html#L96)

tadatuta commented 9 years ago

@mishanga

Опечатка в примере про каскад: .nav__hover => .nav_hover

Исправил, спасибо!

Пост дополню.

ihorzenich commented 9 years ago

Как обещал, дополняю про block__el__el:

Если вам нужно сделать элемент у элемента, значит вам нужно создать новый блок или сделать ваше BEM-дерево с одинарной вложенностью элементов! Вместо:

<div class='block'>
    <div class='block__elem1'>
        <div class='block__elem1__elem2'></div>
    </div>
</div>

Есть два варианта:

№1. Бить на блоки: делать новый блок

<div class='block1'>
    <div class='block2'>
        <div class='block2__elem'></div>
    </div>
</div>

№2. Рубить ветки у вашего BEM-дерева: делать BEM-дерево с 1-ой вложенностью элементов

<div class='block1'>
    <div class='block1__elem1'>
        <div class='block1__elem2'></div>
    </div>
</div>

Типичная ошибка: Попытка вложить имя элемента в имя блока. Чтоб "схитрить" и "как-будто не вложить", написать не block__el1__el2 а blockel1__el2 или block__el1el2. Так нельзя.

/* Так писать нельзя! */
.block {}
.blockel1 {}
.blockel1__el2 {}

Будут проблемы при переносе

<!-- попытались перенести в другое место - получили элемент что завис "в воздухе" без блока-родителя -->
<div class='someblock'>
    <div class='blockel1__el2'></div>
</div>

Такие имена можно делать только если .blockelem сохранит логический смысл при переносе в другой блок.

Обратите внимание - вы не можете вкладывать элементы в элементы в CSS, но можете и должны вкладывать элементы в элементы в HTML! DOM-дерево и BEM-дерево могут быть разными.

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

element__element нельзя в CSS, но можно в HTML!

tadatuta commented 9 years ago

@delka Во втором примере потерялась чиселка возле имени блока.

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

qfox commented 9 years ago

@tadatuta Поправил бы ;-) Ты ж овнер!

ihorzenich commented 9 years ago

@tadatuta спасибо, отредактировал и дополнил коммент твоим чтоб было понятней. Заодно и слайды доклада обновил: http://delka.github.io/talks/wsd/2014/bem/

ihorzenich commented 8 years ago

@mishanga: 1) элементы не должны болтаться в дереве на пределами родителя (когда, например, к блоку миксуются чужие элементы) Элементы могут находится в DOM за пределами своего блока: https://web-standards.slack.com/archives/bem/p1477471042000233

kompolom commented 8 years ago

@tadatuta А как к вам в бэмовый slack попасть?

ihorzenich commented 8 years ago

@kompolom просто регишся под своим мылом тут: https://web-standards.slack.com/messages/bem/ Там доступ открыт для всех.