Блок i-bem.js позволяет работать с несколькими видами событий.
DOM-события
Для работы с DOM i-bem.js использует jQuery, так что и взаимодействие с DOM-событиями происходит уже в нормализованном виде.
Для подписки используется метод bindTo:
someBlock.bindTo('click', handler);
Можно подписаться на событие, которое происходит на DOM-ноде элемента:
someBlock.bindTo('someElem', 'click', handler);
Для отписки служит метод unbindFrom, который первым опциональным параметром принимает элемент и имя события, от которого необходимо отписаться.
Существуют также универсальный метод bindToDomElem($domElem, 'event', fn) и соответствующий ему unbindFromDomElem($elem, 'event') для подписки на любую DOM-ноду.
И методы-хелперы:
bindToDoc / unbindFromDoc — события на document,
bindToWin / unbindFromWin — события на window.
БЭМ события
Для обеспечения слабой связанности между блоками используется шаблон Observer.
Он реализован посредством jQuery-плагина Observable.
БЭМ события можно условно разделить на 2 подтипа: события, которые слушаются на конкретном экзепляре блока и делегированные события.
В первом случае для подписки на событие используется метод on('eventName', handler) и un для отписки.
Для вызова события есть метод trigger('eventName', optionalData).
Объект optionalData будет доступен в обработчике во втором параметре: handler(e, optionalData).
Распространенной практикой является создание БЭМ-оберток над DOM-событиями. Это позволяет использовать делегированные и live-события (о которых ниже) и добавлять какую-то дополнительную логику. Например, если у блока выставлен модификатор disabled, DOM-события все равно будут отрабатывать. В таком случае удобно написать БЭМ-обертку и отменять события в случае наличия модификатора disabled, например, см. реализацию блока button.
Также удобно триггерить события в ответ на установку/снятие модификатора, чтобы другие блоки могли как-то отреагировать на это.
На заметку: Callback-функции, реагирующие на изменение модификатора, выполняются до установки модификатора. Если существует необходимость выполнить часть кода после установки модификатора, нужно воспользоваться методом .afterCurrentEvent().
(Более подробно про модификаторы в JS см. http://bem.github.com/bem
Регулярно возникает необходимость делегировать события родителю. Это более эффективно с точки зрения быстродействия и позволяет отслеживать события на блоках, которых еще не существовало на момент подписки (скажем были сгенерированы на клиенте в результате ajax-запроса).
Для этого служит конструкция
BEM.blocks['b-link'].on($optionalContext, 'eventName', handler);
Пример. Подписываемся на клик по любой ссылке внутри данного блока
BEM.DOM.decl('some-block',
{
onSetMod: {
'js': function() {
BEM.blocks['b-link'].on(this.domElem, 'click', this._onLinkClick);
}
},
_onLinkClick: function(e) {
e.preventDefault();
console.log(e.block.domElem.attr('href'));
}
});
Важно понимать, что 'click' в данном случае — это имя БЭМ-события.
В статических свойствах блока (свойствах класса) предусмотрено специальное свойство live, отвечающее за инициализацию по требованию и за подписку на live события на DOM элементах внутри такого блока.
Если live определено как Function, то эта функция будет выполнена один раз — при попытке инициализации первого такого блока.
Использование live событий позволяет избежать лишнего поиска блоков в DOM дереве. Кроме того, при такой привязке к событию реакция на событие блока из контейнера будет происходить даже в том случае, если на момент привязки блока в контейнере не было, а он появился позже в результате динамического изменения документа.
Синтаксис:
BEM.DOM.decl('some-block',
{
_someHandler: function() {
// some code
}
},
{
live: function() {
this.liveBindTo('click', function(e) {
this._onClick(e);
});
}
});
Здесь важно понимать, что секция live относится к классу блока и, соответственно, не знает ничего о модификаторах.
Даже если в декларации будет указан конкретный модификатор BEM.DOM.decl({ block: 'some-block', modName: 'someMod', modVal: 'someVal' }, {}, { live: true }), секция live будет действовать для всех блоков some-block.
liveInitOnEvent — хелпер для инициализации блока по событию на DOM-элементе блока или его внутреннего элемента.
liveBindTo — подписка на live-события на DOM-элементе блока или его внутреннем элементе.
Оба этих хелпера инициализируют блок при возникновении первого такого события. Различие же заключается в том, что callback функция в liveInitOnEvent вызывается только один раз после инициализации блока, а в liveBindTo она будет вызываться при каждом событии. Контекстом такой callback функции является тот блок, в котором произошло событие.
В вышеприведенном примере блок some-block будет инициализирован при первом клике на себе и будет реагировать на каждый последующий клик.
Если же live определено как Boolean и установлено в true, то такой блок будет инициализирован только при попытке доступа к нему, например, из методов поиска findBlockInside/findBlockOutside.
liveUnbindFrom — хелпер для отписки от live-событий на DOM-элементе блока или его элементах.
liveInitOnBlockEvent — live-инициализация по событию другого блока на DOM-элементе данного.
liveInitOnBlockInsideEvent - live-инициализация по событию блока, вложенного в данный.
liveInitOnBlockInit — live-инициализация по инициализации другого блока на DOM-элементе данного.
liveInitOnBlockInsideInit — live-инициализация по инициализации блока, вложенного в данный
liveCtxBind (и liveCtxUnbind) — Добавляет live-обработчик на блок для переданного элемента, на котором будет слушаться событие (это не DOM-события).
Каналы событий
Каналы реализуют шаблон Mediator.
Существует глобальная функция BEM.channel, которая в качестве параметра получает произвольное название канала и предоставляет интерфейс для публикации и подписки на события в этом именованном канале:
BEM.channel('ololo').on('pysch', function(e, data) {
console.log('Pysch happens');
});
Примером использования канала служит блок i-system.
Он триггерит событие tick в канал sys каждые 50 миллисекунд, если пользователь проявляет какую-то активность на странице. Если более 3 секунд пользователь никак не проявился, вызывается событие idle. При возобновлении пользовательской активности триггерится wakeup и далее снова tick.
Это позволяет любым блокам при необходимости использовать общий таймер и «засыпать», если пользователь, например, переключился на другую вкладку.
Также удобно использовать каналы для публикации событий о получении данных от сервера, когда множество блоков на странице зависит от этих данных.
// возможно в примере _onLinkClick очепятка -- s/e.block.domElem/e.data.domElem/g .
Найти блок, который тянет за собой i-system, просто:
По поводу опечатки проверил, в тексте все верно — элемент передается в поле event.block.domElem.
оказывается таймер прилетел вместе с input из bl-controls.
а название свойства e.block.domElem в обработчике .on(), я перепутал со свойством ивента, прилетающего в случае .bindTo().