Пользователь
0,0
рейтинг
5 октября 2013 в 03:48

Разработка → jQuery.BEM — декларативный подход к работе с версткой по БЭМ методу из песочницы

Уже много всего сказано про БЭМ, чем он хорош, чем плох, и повторяться сегодня мы не будем. Ниже рассказ про то, как работать с БЭМ DOM из jQuery в небольших проектах, где совесть и время не позволяют прикрутить bem-tools, bem-bl и bemhtml, а поработать с удобной системой верстки все-же хочется, оставив позади длинные селекторы в js файлах.

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


Методология


БЭМ (аббр. Блок Элемент Модификатор) — особый способ организации HTML верстки, сильно облегчающий разработку и поддержку более-менее крупных сайтов и веб-приложений.

Полностью вводить вас в курс дела по методологии я не буду, в последнее время она становится очень популярной и материалов, откуда можно почерпнуть информацию предостаточно. Но если вышло так, что вы все еще не слышали о БЭМ и его преимуществах, рекомендую заглянуть:



Непосвященным вдвойне рекомендую посмотреть лекцию, во первых: не надо ничего читать, там видео, во вторых: объяснено все куда лучше первого варианта и мозги вправляет на ура.

Зачем еще одна реализация?


Не сказать что подобных штуковин так уж и много, единственный близкий аналог — фреймворк i-bem из библиотеки bem-bl, но заставить его работать вне родной среды обитания будет довольно проблематично.

Яндексовские bem-tools, bem-bl и вся возня со сборщиками и шаблонизаторами это, несомненно, круто. Но пока мы не доросли до проектов, имеющих пару сотен шаблонов, все это будет скорее мешать. Предположим, под рукой мы имеем маленький интернет-магазинчик, на каком-нибудь легеньком PHP-фреймворке. Здесь все это — стрельба из пушки по воробьям, однако рядом всегда будет любимый заказчик, которому все время приспичит что-нибудь поменять.

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

Я не утверждаю, что jQuery.BEM — самая лучшая реализация сего действа, она вполне себе может проигрывать в производительности тому-же i-bem из за сложных путешествий по DOM, но зато искоробочная и, на данный момент, самая удобная.

Работа с элементами


Предположим, у нас есть несколько блоков с товарами:

<div class="b-product">
	<div class="b-product__name">Coffe</div>
	<div class="b-product__price">$2</div>
</div>
<div class="b-product">
	<div class="b-product__name">Tea</div>
	<div class="b-product__price">$1</div>
</div>


Нам нужно произвести какое-либо действие с дочерними элементами первого блока:

$('.b-product').first().children('.b-product__name'); // jQuery
$('.b-product').first().elem('name');                 // jQuery.BEM


С jQuery.BEM запись вышла короче. Разница в пару байт это, конечно, сомнительное приемущество, но в продакшене .elem() облегчает жизнь, если блок, например, нужно внезапно переименовать.

Работа с модификаторами


Добавляем модификатор

<div class="b-product">...</div>

$('.b-product').setMod('theme', 'premium'); // получим .b-product.b-product_theme_premium
$('.b-product').setMod('premium');          // получим .b-product.b-product_premium_yes


Удаляем модификатор

<div class="b-product b-product_theme_premium">...</div>

$('.b-product').delMod('theme', 'discount'); // несуществующий модификатор - ничего не изменится .b-product.b-product_theme_premium
$('.b-product').delMod('theme', 'premium');  // снесет класс .b-product_theme_premium
$('.b-product').delMod('theme');             // снесет модификатор theme с любым значением


Получаем модификатор

<div class="b-product b-product_theme_premium">...</div>

$('.b-product').getMod('theme');    // вернет "premium"
$('.b-product').getMod('discount'); // вернет null


Проверяем модификатор

<div class="b-product b-product_theme_premium">...</div>

$('.b-product').hasMod('theme');             // true
$('.b-product').hasMod('theme', 'premium');  // true
$('.b-product').hasMod('theme', 'discount'); // false


Фильтруем блоки по модификатору

<div class="b-product b-product_theme_premium">...</div>
<div class="b-product">...</div>

$('.b-product').byMod('theme', 'premium'); // вместо $('.b-product.b-product_theme_premium')
$('.b-product').byMod('theme');            // вместо $('.b-product.b-product_theme_premium')
$('.b-product').byNotMod('theme');         // вместо $('.b-product').not('.b-product_theme_premium')


Декларации блоков



Помимо системы именования классов, киллер-фичей БЭМ являются декларации. Они позволяют здорово облегчить работу с логикой блоков хотя-бы просто визуально отделяя их друг от друга.

bem.decl('b-product', {
	onInit: function($this) {
		// Добавляем элементу price модификатор size со значением large как только DOM будет готов
		// $this внутри функии — обычный jQuery объект
		$this.elem('price').setMod('size', 'large');
	},
	title: {
		onMouseover: function($this) {
			// Добавляем блоку b-product модификатор state со значением hover
			$this.up().setMod('state', 'hover');

			// Вызываем какую-нибудь вспомогательную функцию
			this._customFunction();
		},
		_customFunction: function() {
			console.log('I am helper function');
		}
	},
	price: {
		onSetmod: function($this, e, modKey, modVal) {
			// Выводим в консоль информацию об установленном модификаторе элементу price
			console.log('Modifier set:', modKey, modVal);
		},
		onDelmod($this, e, modKey, modVal) {
			//
		}
	}
});


Можно декларировать только блоки или элементы с определенными модификаторами, а так же отдельные элементы блока:

bem.decl({ block: 'b-product', mod: 'theme:premium'}, {
	//
});

bem.decl({ block: 'b-product', elem: 'price' }, {
	//
});


Все события являются всплывающими и вешаются на document методом $(document).on(...), так что переопределять декларации, при изменении DOM на стороне клиента, не нужно.

Блоки могут общаться между собой событиями, функция начинающаяся с «on...», вешает на объект обработчик, так что в любом месте можно сделать $('.b-block').elem('title').trigger('on...');

Переопределение синтаксиса


По умолчанию, синтаксические параметры следуют оригинальному БЭМ. Если нужно избавиться от необходимости использования префиксов (b-, l-, g-, etc) или изменить разделитель блока и элемента с "__" на что-нибудь более удобное, используйте:

bem.setConfig({
	namePrefix: '',     // Префикс блока, можно регуляркой
	elemPrefix: '-',    // Префикс элемента
	modPrefix: '_',     // Префикс модификатора
	modDlmtr: '-'       // Разделитель модификатора и значения
});


Теперь имена классов в нашем HTML должны выглядеть немного по другому.

.block
.block-element
.block-element_modifier-value


В заключение


Плагин, на условиях лицензии MIT, доступен на GitHub. Буду рад, если он кому-нибудь облегчит жизнь и принесет пользу. Всегда рад аргументированной критике и вопросам.
Максим Полетаев @zenwalker
карма
5,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (21)

  • 0
    Интересно! Особенно setMod и delMod, вот только почему два модификатора записываются как «b-product b-product_theme_premium», а не «b-product b-product_theme b-product_premium»? Второй вариант не поддерживается совсем?
    • +3
      Это не два модификатора, а один. Модификатор theme со значением premium (theme = premium).
  • +2
    • 0
      • 0
        Обидно, что все это не нагуглилось, когда я брался за свой велосипед.
        • +1
          Две первые ссылки по запросу «jquery bem», но нет ничего плохого в велосипедах.
          • 0
            В тот момент их не было, как ни странно.
  • +1
    Не совсем по теме, но:

    $('.b-product').first().children('.b-product__name')
    V
    $('.b-product:first > .b-product__name')
    • 0
      Селекторы работают справа налево и в вашем случае мы получаем лишние операции.
      • +1
        При равных контекстах, селектор отработает на порядок быстрее, даже учитывая специфику его работы. И плюс, что даже важнее, селектор предпочтительнее из-за SOLID — и нам не нужно мешать javascript и селекторы, как в первом случае. + jsperf
        • 0
          Сам как раз тестировал на примере из статьи.
          В общем, не совсем понял, как у вас так получилось: jsperf.com/bem-like-selector-test

          Кстати, библиотека из топика работает очень быстро.

          Хотя, у меня мало опыта в тестах…
        • 0
          Понял, в чем дело.
          :first-child работает в разы быстрее, чем :first

          Так что самый быстрый вариант, это
          $('.b-product:first-child > .b-product__name')
          • 0
            :first-child правильный псевдо класс для document.querySelector(''). Но здесь более важна не производительность, а сама запись. Приятнее видеть целенаправленный селектор (в местах где это возможно), чем цепочки вызовов first/children/last.
  • 0
    На моей прошлой работе сотрудник написал подобное только с jquery ui github.com/hunterman/jquery-ui-bem-block
  • 0
    единственный близкий аналог — фреймворк i-bem из библиотеки bem-bl, но заставить его работать вне родной среды обитания будет довольно проблематично.

    Можете написать подробнее, с какими сложностями вы столкнулись при подключении i-bem.js?
    • 0
      Просто так, как обыкновенный плагин его не подключишь. Блок нужно собрать, либо с помощью того-же bem-tools, либо ручками, по кусочкам, исходя из зависимостей, что не удобно и как-бы не по правилам.
      • 0
        Правильно я понимаю, что нельзя просто написать <script src=... /> и после этого подключить скрипты для блоков?
        Мне раньше казалось, что ему должно быть без разницы, каким образом был собран блок — главное, чтобы соблюдались наименования классов.
        Можете привести конкретный пример, где не будет работать?
        • 0
          Под сборкой блока я подразумевал сборку самого i-bem (по сути это тоже блок).

          Работать скорее всего будет, просто помимо i-bem.js вам нужно будет подключать еще файлы, от которых он зависит, например некоторые плагины из i-jquery, либо заставить bem-tools это сделать. Скажем так, мне было не сразу понятно, как его заставить работать, чего он хочет и откуда это взять, в этом и проблема.
  • 0
    delete
  • 0
    «bem-tools, bem-bl и вся возня со сборщиками и шаблонизаторами это, несомненно, круто. Но пока мы не доросли до проектов, имеющих пару сотен шаблонов, все это будет скорее мешать.»

    А что мешает взять отдельно i-bem и писать js, используя его. Руками придется добавлять к каждому блоку класс «i-bem» и параметры в «data-bem» только и всего.
  • 0
    A bem-tools и для небольших проектов хорош, ничего там страшного нет www.slideshare.net/Alex_Baum/alex-baumgertner-beminsmallprojects

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.