Войти с помощью github
Форум /

Блок i-bem.js позволяет работать с несколькими видами событий.

DOM-события


Для работы с DOM i-bem.js использует jQuery, так что и взаимодействие с DOM-событиями происходит уже в нормализованном виде.

Для подписки используется метод bindTo:

someBlock.bindTo('click', handler);


Можно подписаться на событие, которое происходит на DOM-ноде элемента:

someBlock.bindTo('someElem', 'click', handler);

 

Следует отметить, что любое взаимодействие с элементами в БЭМ-терминах происходит через методы блока-родителя. Например, для установки/снятия модификаторов у элемента, необходимо будет вызвать соответствующие методы блока и передать элемент в качестве параметра. А методы, возвращающие элементы (elem, findElem), на самом деле возвращают jQuery-коллекции, которые ничего о предметной области БЭМ, разумеется, не знают.



Для отписки служит метод 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-bl/sets/common-desktop/i-bem/i-bem.ru.html#Reakciyanaizmeneniemodifikatorov).



Регулярно возникает необходимость делегировать события родителю. Это более эффективно с точки зрения быстродействия и позволяет отслеживать события на блоках, которых еще не существовало на момент подписки (скажем были сгенерированы на клиенте в результате 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.

Это позволяет любым блокам при необходимости использовать общий таймер и «засыпать», если пользователь, например, переключился на другую вкладку.

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