The ym module system (modules.define/modules.require) has been replaced with native ES modules.
Before:
modules.define('my-block', ['i-bem-dom', 'events'], function(provide, bemDom, events) {
provide(bemDom.declBlock(this.name, { /* ... */ }));
});After:
import bemDom from 'bem:i-bem-dom';
import events from 'bem:events';
export default bemDom.declBlock('my-block', { /* ... */ });All bem:* imports are resolved at build time by vite-plugin-bem-levels to the actual file paths, respecting platform-specific level priorities.
Module redefinitions that previously used modules.define with a callback receiving the previous module value are now handled via barrel files generated by the Vite plugin.
Before (ym redefinition):
modules.define('jquery', function(provide, $) {
$.event.special.pointerclick = { /* ... */ };
provide($);
});After (side-effect import in barrel):
// The barrel file auto-generated by vite-plugin-bem-levels:
import $ from '../../common.blocks/jquery/jquery.js';
import '../../common.blocks/jquery/__event/_type/jquery__event_type_pointerclick.js';
export default $;The jquery peer dependency is now ^4.0.0.
Key breaking changes in jQuery 4:
$.unique()has been removed. Use$.uniqueSort().- Several deprecated methods have been removed. See jQuery 4.0 upgrade guide.
The vow dependency has been removed. All asynchronous code now uses native Promise.
Before:
var vow = require('vow');
var promise = vow.resolve(value);After:
const promise = Promise.resolve(value);The entire ENB toolchain has been replaced with Vite.
Before:
./node_modules/.bin/enb makeAfter:
npm run build # both platforms
npm run build:desktop # desktop only
npm run build:touch # touch onlyThe Vite config is in build/vite.config.js. The custom vite-plugin-bem-levels plugin in build/plugins/ handles BEM level scanning and module resolution.
The minimum supported Node.js version is now 20 (was 8). Update your .nvmrc or CI configuration accordingly.
Replace any custom .jshintrc or .jscs.json rules with ESLint flat config (eslint.config.js).
- Server-side tests:
mocha/chaihave been replaced withnode:test/node:assert. - Browser tests:
mocha-phantomjshas been replaced with Playwright. Run withnpm run test:browser. - All tests:
npm run test:allruns both server-side and browser tests.
Replace .travis.yml with .github/workflows/ci.yml. The new CI runs lint, test, test:browser, and build jobs.
Replace .githooks/ with husky configuration. The prepare script in package.json sets up husky automatically on npm install.
The dom element of the i-bem block was moved to a separate i-bem-dom block.
Before:
modules.define('my-dom-block', ['i-bem__dom'], function(provide, BEMDOM) {
/* ... */
});After:
modules.define('my-dom-block', ['i-bem-dom'], function(provide, bemDom) {
/* ... */
});The i-bem and i-bem-dom blocks are no longer classes. They are modules with methods for declaring BEM entities, links to classes of BEM entities, and some additional helpers. These methods are no longer class methods for the corresponding blocks.
Issue: #413.
Instead of the decl() method, use the declBlock() method to declare a block.
Before:
modules.define('my-dom-block', ['i-bem__dom'], function(provide, BEMDOM) {
provide(BEMDOM.decl(this.name, { /* ... */ }));
});After:
modules.define('my-dom-block', ['i-bem-dom'], function(provide, bemDom) {
provide(bemDom.declBlock(this.name, { /* ... */ }));
});Instead of the static decl() method, use the static declMod() method to declare a modifier.
Before:
modules.define('my-dom-block', function(provide, MyDomBlock) {
provide(MyDomBlock.decl({ modName : 'my-mod', modVal : 'my-val' }, { /* ... */ }));
});After:
modules.define('my-dom-block', function(provide, MyDomBlock) {
provide(MyDomBlock.declMod({ modName : 'my-mod', modVal : 'myVal' }, { /* ... */ }));
});Before:
modules.define('my-dom-block', function(provide, MyDomBlock) {
provide(MyDomBlock.decl({ modName : 'my-mod', modVal : 'true' }, { /* ... */ }));
});After:
modules.define('my-dom-block', function(provide, MyDomBlock) {
provide(MyDomBlock.declMod({ modName : 'my-mod' }, { /* ... */ }));
});Issue: #1374.
Before:
modules.define('my-dom-block', function(provide, MyDomBlock) {
provide(MyDomBlock.decl({ modName : 'my-mod' }, { /* ... */ }));
});After:
modules.define('my-dom-block', function(provide, MyDomBlock) {
provide(MyDomBlock.declMod({ modName : 'my-mod', modVal : '*' }, { /* ... */ }));
});Issue: #1376.
Instead of the decl() method for a block class, use the declBlock() method for the i-bem-dom module.
Before:
modules.define('my-dom-block', function(provide, MyDomBlock) {
provide(MyDomBlock.decl({ /* ... */ }));
});After:
modules.define('my-dom-block', ['i-bem-dom'], function(provide, bemDom, MyDomBlock) {
provide(bemDom.declBlock(MyDomBlock, { /* ... */ }));
});Before:
modules.define('my-dom-block', ['i-bem__dom', 'my-base-dom-block'], function(provide, BEMDOM, MyBaseDomBlock) {
provide(BEMDOM.decl({ block : this.name, baseBlock : MyBaseDomBlock }, { /* ... */ }));
});After:
modules.define('my-dom-block', ['i-bem-dom', 'my-base-dom-block'], function(provide, bemDom, MyBaseDomBlock) {
provide(bemDom.declBlock(this.name, MyBaseDomBlock, { /* ... */ }));
});The declMix method has been renamed to declMixin. This clarifies the concept of mixes of multiple BEM entities on a single DOM node as opposed to JS-level mixins.
Before:
modules.define('my-mix-block', ['i-bem__dom'], function(provide, BEMDOM) {
provide(BEMDOM.declMix(this.name, { /* ... */ }));
});After:
modules.define('my-mixin-block', ['i-bem-dom'], function(provide, bemDom) {
provide(bemDom.declMixin({ /* ... */ }));
});Before:
modules.define('my-dom-block', ['i-bem__dom', 'my-mix-1', 'my-mix-2'], function(provide, BEMDOM) {
provide(BEMDOM.decl({ block : this.name, baseMix : ['my-mix-1', 'my-mix-2']}, { /* ... */ }));
});After:
modules.define('my-dom-block', ['i-bem-dom', 'my-mixin-1', 'my-mixin-2'], function(provide, bemDom, MyMixin1, MyMixin2) {
provide(bemDom.declBlock(this.name, [MyMixin1, MyMixin2], { /* ... */ }));
});When declaring a specific modifier (for example, _my-mod_my-val), it wasn't possible to declare the behavior for deleting this modifier. We had to make two declarations.
Before:
//
modules.define('my-dom-block', function(provide, MyDomBlock) {
MyDomBlock.decl({
onSetMod : {
'my-mod' : {
'' : function() { /* ... */ } // declaration for deleting the _my-mod_my-val modifier
}
}
});
provide(MyDomBlock.decl({ modName : 'my-mod', modVal : 'my-val' }, { /* ... */ }));
});After:
modules.define('my-dom-block', function(provide, MyDomBlock) {
provide(MyDomBlock.declMod({ modName : 'my-mod', modVal : 'my-val' }, {
onSetMod : {
'mod1' : {
'' : function() { /* ... */ } // declaration for deleting the _my-mod_my-val modifier
}
}
}));
});Issue: #1025.
Shorthand syntax is now available for declaring behaviors for changing modifiers.
Before:
onSetMod : {
'my-mod' : {
'*' : function(modName, modVal, prevModVal) {
if(prevModVal === 'my-val') {
/* ... */ // declaration for changing _my-mod_my-val to any other value
}
}
}
}After:
onSetMod : {
'my-mod' : {
'~my-val' : function() { /* ... */ } // declaration for changing the my-mod value from my-val to any other value
}
}
}Before:
onSetMod : {
'my-mod' : {
'*' : function(modName, modVal) {
if(modVal !== 'my-val') {
/* ... */ // declaration for changing my-mod to any value other than my-val
}
}
}
}After:
onSetMod : {
'my-mod' : {
'!my-val' : function() { /* ... */ } // declaration for changing my-mod to any value other than my-val
}
}
}Issue: #1072.
The functionality of the live field has been divided into two parts: the lazyInit field and the onInit() method.
Before:
modules.define('my-dom-block', ['i-bem__dom'], function(provide, BEMDOM) {
provide(BEMDOM.decl(this.name, { /* ... */ }, {
live : true
}));
});After:
modules.define('my-dom-block', ['i-bem-dom'], function(provide, bemDom) {
provide(bemDom.declBlock(this.name, { /* ... */ }, {
lazyInit : true
}));
});Before:
modules.define('my-dom-block', ['i-bem__dom'], function(provide, BEMDOM) {
provide(BEMDOM.decl(this.name, { /* ... */ }, {
live : function() {
/* ... */
}
}));
});After:
modules.define('my-dom-block', ['i-bem-dom'], function(provide, bemDom) {
provide(bemDom.declBlock(this.name, { /* ... */ }, {
lazyInit : true,
onInit : function() {
/* ... */
}
}));
});Before:
modules.define('my-dom-block', ['i-bem__dom'], function(provide, BEMDOM) {
provide(BEMDOM.decl(this.name, { /* ... */ }, {
live : function() {
/* ... */
return false;
}
}));
});After:
modules.define('my-dom-block', ['i-bem-dom'], function(provide, bemDom) {
provide(bemDom.declBlock(this.name, { /* ... */ }, {
onInit : function() {
/* ... */
}
}));
});Before:
{
block : 'b1',
js : { live : false }
}After:
{
block : 'b1',
js : { lazyInit : false }
}Issue: #877.
Deleted the elem-instances element of the i-bem block and the elem-instances modifier of the dom element in the i-bem block.
Now the corresponding functionality is incorporated into i-bem and i-bem-dom.
Before:
modules.define('my-dom-block__my-elem', ['i-bem__dom'], function(provide, BEMDOM) {
provide(BEMDOM.decl({ block : 'my-dom-block', elem : 'my-elem' }, { /* ... */ }));
});After:
modules.define('my-dom-block__my-elem', ['i-bem-dom'], function(provide, bemDom) {
provide(bemDom.declElem('my-dom-block', 'my-elem', { /* ... */ }));
});Now the _elem(elemName) method of the block instance (previously elem(elemName)) returns an instance of the element's class, instead of a jQuery object with all the elements named elemName.
To get a collection of instances of the element's class, use the _elems() method.
Now the caches for elements with JS implementation found with _elem() and _elems() are invalidated automatically when the DOM is modified.
Issue: #1352.
Note: When this methods are used for elements without JS implementation you still need to use _dropElemCache() in cases of dynamically DOM update.
Note: don't forget to switch on support for elements instances in template engine.
For bem-xjst please refer to https://github.com/bem/bem-xjst/blob/master/docs/en/3-api.md#support-js-instances-for-elements-bem-core-v4 or for BH see https://github.com/bem/bh#jselem.
Before:
this.setMod(this.elem('my-elem'), 'my-mod', 'my-val');After:
this._elem('my-elem').setMod('my-mod', 'my-val');The same is true for the methods getMod(), hasMod(), toggleMod(), and delMod().
The following methods were deleted from the block API: elemify(), elemParams(), and the onElemSetMod field. The corresponding functionality is provided in instances of elements.
Also see the changes for search methods.
Issue: #581.
Renamed the following methods:
findBlockInside()tofindChildBlock()findBlocksInside()tofindChildBlocks()findBlockOutside()tofindParentBlock()findBlocksOutside()tofindParentBlocks()findBlockOn()tofindMixedBlock()findBlocksOn()tofindMixedBlocks()
The optional first parameter about the element has been removed from these methods.
Added the following methods: findChildElem(), findChildElems(), findParentElem(), findParentElems(), findMixedElem(), findMixedElems().
Before:
this.findBlockInside(this.elem('my-elem'), 'my-block-2');After:
this.findChildElem('my-elem').findChildBlock(MyBlock2);Deleted the following methods: findElem(), closestElem(). Use the findChildElem() and findParentElem() elements, instead.
The methods findChildBlocks(), findParentBlocks(), findMixedBlocks(), findChildElems(), findParentElems(), and findMixedElems() return collections of BEM entities.
The findChildElem() and findChildElems() methods (unlike the previous equivalent findElem) don't search on their own DOM nodes of the instance.
Before:
this.findElem('my-elem');After:
this.findChildElems('my-elem').concat(this.findMixedElems('my-elem'));However, consider whether you really need both searches. In most cases, you can just use either this.findChildElems('my-elem') or this.findMixedElems('my-elem').
In place of the deleted containsDomElem() method, use the containsEntity() method.
Before:
this.containsDomElem(someElem);After:
this.containsEntity(someElem);The functionality of the collection element of the i-bem block is no longer optional.
All methods that return an array of BEM entities now return collections.
Before:
this.findBlocksInside('my-block-2')[0].setMod('my-mod', 'my-val');After:
this.findChildBlocks(MyBlock2).get(0).setMod('my-mod', 'my-val');Before:
this.findBlocksInside('my-block-2').forEach(function(myBlock2) {
return myBlock2.setMod('my-mod', 'my-val');
});After:
this.findChildBlocks(MyBlock2).setMod('my-mod', 'my-val');Issue: #582.
The events API has been simplified. Deleted the following block instance methods: on(), un(), once(), bindTo(), unbindFrom(), bindToDoc(), bindToWin(), unbindFromDoc(), unbindFromWin(), and class methods: liveBindTo(), liveUnbindFrom(), on(), un(), once(), liveInitOnBlockEvent(), liveInitOnBlockInsideEvent().
They have been replaced with the new _domEvents() and _events() methods, which return an instance of the events manager class with the on(), un() and once() methods.
Before:
BEMDOM.decl('my-block', {
onSetMod : {
'js' : {
'inited' : function() {
this.bindTo('click', this._onClick);
}
}
}
});After:
bemDom.declBlock('my-block', {
onSetMod : {
'js' : {
'inited' : function() {
this._domEvents().on('click', this._onClick);
}
}
}
});Before:
BEMDOM.decl('my-block', {
onSetMod : {
'js' : {
'inited' : function() {
this.bindToDoc('click', this._onDocClick);
}
}
}
});After:
bemDom.declBlock('my-block', {
onSetMod : {
'js' : {
'inited' : function() {
this._domEvents(bemDom.doc).on('click', this._onDocClick);
}
}
}
});Before:
BEMDOM.decl('my-block', {
onSetMod : {
'js' : {
'inited' : function() {
this.bindToWin('resize', this._onWinResize);
}
}
}
});After:
bemDom.declBlock('my-block', {
onSetMod : {
'js' : {
'inited' : function() {
this._domEvents(bemDom.win).on('resize', this._onWinResize);
}
}
}
});If an event was fired on BEM instance the event object will contain a link to an instance:
this._domEvents('my-elem').on('click', function(e) {
e.bemTarget // refers to `my-elem` instance
});Before:
BEMDOM.decl('my-block', {
onSetMod : {
'js' : {
'inited' : function() {
this.findBlockOutside('my-block-2').on('my-event', this._onMyBlock2MyEvent, this);
},
'' : function() {
this.findBlockOutside('my-block-2').un('my-event', this._onMyBlock2MyEvent, this);
}
}
}
});After:
bemDom.declBlock('my-block', {
onSetMod : {
'js' : {
'inited' : function() {
this._events(this.findParentBlock('my-block-2')).on('my-event', this._onMyBlock2MyEvent);
}
}
}
});Note that unsubscribing from events is now automatic when the instance is destroyed.
Before:
BEMDOM.decl('my-block', { /* ... */ }, {
live : function() {
this.liveBindTo('click', this.prototype._onClick);
}
});After:
bemDom.declBlock('my-block', { /* ... */ }, {
onInit : function() {
this._domEvents().on('click', this.prototype._onClick);
}
});Before:
BEMDOM.decl('my-block', { /* ... */ }, {
live : function() {
this.liveBindTo('my-elem', 'click', this.prototype._onMyElemClick);
}
});After:
bemDom.declBlock('my-block', { /* ... */ }, {
onInit : function() {
this._domEvents('my-elem').on('click', this.prototype._onMyElemClick);
}
});Before:
BEMDOM.decl('my-block', { /* ... */ }, {
live : function() {
this.liveInitOnBlockInsideEvent('my-event', 'my-block-2', this.prototype._onMyBlock2MyEvent);
}
});After:
bemDom.declBlock('my-block', { /* ... */ }, {
onInit : function() {
this._events(MyBlock2).on('my-event', this.prototype._onMyBlock2MyEvent);
}
});Note that the parameter with the event handler function is now required.
Before:
modules.define('my-block', ['i-bem__dom', 'my-block-2'], function(provide, BEMDOM) {
provide(BEMDOM.decl(this.name, { /* ... */ }, {
live : function() {
this.liveInitOnBlockInsideEvent('my-event', 'my-block-2');
}
}));
});After:
modules.define('my-block', ['i-bem-dom', 'my-block-2', 'functions'], function(provide, bemDom, MyBlock2, functions) {
provide(bemDom.declBlock(this.name, { /* ... */ }, {
onInit : function() {
this._events(MyBlock2).on('my-event', functions.noop);
}
}));
});Before:
BEMDOM.decl('my-block', {
onSetMod : {
'js' : {
'inited' : function() {
MyBlock2.on(this.domElem, 'my-event', this._onMyBlock2MyEvent, this);
},
'' : function() {
MyBlock2.un(this.domElem, 'my-event', this._onMyBlock2MyEvent, this);
}
}
}
});After:
bemDom.declBlock('my-block', {
onSetMod : {
'js' : {
'inited' : function() {
this._events(MyBlock2).on('my-event', this._onMyBlock2MyEvent);
}
}
}
});Note that unsubscribing from events is now automatic when the instance is destroyed.
Now the bem() method of a jQuery object accepts a BEM class instead of a string.
Before:
modules.require(['jquery', 'i-bem__dom'], function($, BEMDOM) {
var myBlock = $('.my-block').bem('my-block');
});After:
modules.require(['jquery', 'my-block'], function($, MyBlock) {
var myBlock = $('.my-block').bem(MyBlock);
});Before:
modules.require(['jquery', 'i-bem__dom'], function($, BEMDOM) {
$('.my-block').bem('my-block').on('my-event', function() { /* ... */ });
});After:
modules.require(['jquery', 'my-block', 'events__observable'], function($, MyBlock, observable) {
observable($('.my-block').bem(MyBlock))
.on('my-event', function() { /* ... */ });
});In addition, you must add { block : 'events', elem : 'observable', mods : { type : 'bem-dom' } } to the dependency.
Issue: #394.
Renamed the protected methods:
emit()to_emit()elem()to_elem()dropElemCache()to_dropElemCache()buildClass()to_buildClassName()buildSelector()to_buildSelector()getDefaultParams()to_getDefaultParams()
Deleted the getMods() method.
The querystring__uri element is now the uri block. The querystring block is now the uri__querystring element.
Issue: #967.
The page__css element does not support ie field. Use the page__conditional-comment element instead.
Before:
{
block : 'page',
head : [
{ elem : 'css', url : 'my-css.css', ie : false },
{ elem : 'css', url : 'my-css', ie : true }
],
content : 'Page content'
}
After:
{
block : 'page',
head : [
{
elem : 'conditional-comment',
condition : '! IE',
content : { elem : 'css', url : 'my-css.css' }
},
{
elem : 'conditional-comment',
condition : '> IE 8',
content : { elem : 'css', url : 'my-css.ie.css' }
}
// and so on for needed IE versions
],
content : 'Page content'
}
Issue: #379.
To migrate to version 3.0.0, review the history of changes.
To migrate to version 2.0.0, review the history of changes.
For version 1.0.0, migrating requires switching from using bem-bl to using bem-core.
The entire code is now written in terms of the modular system https://github.com/ymaps/modules. All dependencies must be explicitly stated in the code. Minimize or eliminate use of global variables, if possible.
Example:
modules.define(
'my-module', // module name
['module-from-library', 'my-another-module'], // module dependencies
function(provide, moduleFromLibrary, myAnotherModule) { // module declaration, called when all dependencies are resolved
// module representation
provide({
myModuleMethod : function() {}
});
});TODO: Add information about the build process (usage of special technologies for JS and instructions for custom builders).
jQuery is represented by a jquery wrapper module that uses the global jQuery object if it already exists on the page, or loads it otherwise.
jQuery is now used only for operations directly related to the DOM (searching for elements, binding listeners to events, setting and getting attribute values, and so on).
All other operations have corresponding modules that provide the same functionality without depending on jQuery:
- The
objectsmodule for operating on objects (with theextend,isEmpty, andeachmethods). - The
functionsmodule for operating on functions (with theisFunctionandnoopmethods).
In addition, all the jQuery plugins that aren't directly related to jQuery ($.observable, $.inherit, $.cookie, $.identify, $.throttle) are now modules:
- The
eventsmodule replaces$.observablefor working with events. It provides the "classes"EventsEmitterandEvent. - The
inheritmodule instead of$.inheritfor working with "classes" and inheritance. - The
cookiemodule instead of$.cookie. - The
identifymodule instead of$.identify. - The
functions__throttleandfunctions__debouncemodules instead of$.throttleand$.debounce.
Before:
// block code
$.throttle()
// block codeAfter:
module.define('my-module', ['functions__throttle'], function(provide, throttle) {
// module code
throttle()
// module code
});Instead of a declaration via BEM.DOM.decl, you need to extend the i-bem__dom module.
Before:
BEM.DOM.decl('block', /* ... */);After:
modules.define('i-bem__dom', function(provide, BEMDOM) {
BEMDOM.decl('block', /* ... */);
provide(BEMDOM);
});You must use full notation for the handler for setting the js modifier to inited.
Before:
onSetMod : {
js : function() {
// constructor code
}
}After:
onSetMod : {
'js' : {
'inited' : function() {
// constructor code
}
}
}Instead of the destruct method, you need to use the handler for setting the js modifier to an empty value (remove the modifier).
You no longer need to call __base in order to run the base destructor defined in i-bem__dom on blocks.
Before:
destruct : function() {
this.__base.apply(this, arguments);
// destructor code
}After:
onSetMod : {
js : {
'' : function() {
// destructor code
}
}
}Instead of the changeThis method, use either the corresponding parameter, or the native bind method if there isn't a parameter.
Before:
// block code
obj.on('event', this.changeThis(this._method));
// block codeAfter:
obj.on('event', this._method.bind(this));
// or better
obj.on('event', this._method, this);Instead of the afterCurrentEvent method, use the nextTick method, which guarantees that the block still exists during the callback (if the block has already been destroyed by this time, the callback isn't executed).
Before:
BEM.DOM.decl('block', {
method : function() {
this.afterCurrentEvent(function() {
/* ... */
})
}
});After:
modules.define('i-bem__dom', function(provide, BEMDOM) {
BEMDOM.decl('block', {
method : function() {
this.nextTick(function() {
/* ... */
});
}
});
});The context for finding an element is no longer set as a string. Instead, pass a jQuery object.
Before:
var nestedElem = this.findElem('parent-elem', 'nested-elem');After:
var nestedElem = this.findElem(this.findElem('parent-elem'), 'nested-elem'),
oneMoreElem = this.findElem(this.elem('another-elem'), 'nested-elem');The liveBindTo method no longer supports the elemName field for passing the element name. Use the elem field instead.
A DOM element that had an event handler bound to it is now accessed as $(e.currentTarget) instead of e.data.domElem.
Before:
onClick : function(e) {
e.data.domElem.attr(/* ... */);
}After:
onClick : function(e) {
$(e.currentTarget).attr(/* ... */);
}Channels are no longer an integral part of BEM. Now they are separate events__channels modules.
Before:
BEM.DOM.decl('block', {
method : function() {
BEM.channel('channel-name').on(/* ... */);
}
});After:
modules.define('i-bem__dom', ['events__channels'], function(provide, channels, BEMDOM) {
BEMDOM.decl('block', {
method : function() {
channels('channel-name').on(/* ... */);
}
});
});This block and channel no longer exist. They have been replaced with separate modules: tick with the "tick" event, and idle with the "idle" and "wakeup" events.
Before:
BEM.DOM.decl('block', {
method : function() {
BEM.channel('sys').on('tick', /* ... */);
}
});After:
modules.define('i-bem__dom', ['tick'], function(provide, tick, BEMDOM) {
BEMDOM.decl('block', {
method : function() {
tick.on('tick', /* ... */);
}
});
});Before:
BEM.DOM.decl('block', {
method : function() {
BEM.channel('sys').on('wakeup', /* ... */);
}
});After:
modules.define('i-bem__dom', ['idle'], function(provide, idle, BEMDOM) {
BEMDOM.decl('block', {
method : function() {
idle.on('wakeup', /* ... */);
}
});
});BEM blocks that were used as storage for some methods but that didn't use the BEM methodology in any way can now be written as modules.
Before:
BEM.decl('i-router', {
route : function() { /* ... */ }
});After:
modules.define('router', function(provide) {
provide({
route : function() { /* ... */ }
});
});If for some reason you need BEM blocks (not BEM.DOM blocks), you can declare them by extending the i-bem module.
Before:
BEM.decl('my-block', { /* ... */ });After:
modules.define('i-bem', function(provide, BEM) {
BEM.decl('my-block', { /* ... */ });
provide(BEM);
});Before:
BEM.DOM.decl('b-spin', {
onSetMod : {
'js' : function() {
this._size = this.getMod('size') || /[\d]+/.exec(this.getMod('theme'))[0];
this._bgProp = 'background-position';
this._posPrefix = '0 -';
if (this.elem('icon').css('background-position-y')) { /* In IE, you can't get the background-position property. You can only get background-position-y, so use this workaround */
this._bgProp = 'background-position-y';
this._posPrefix = '-';
}
this._curFrame = 0;
this.hasMod('progress') && this.channel('sys').on('tick', this._onTick, this);
},
'progress' : {
'yes' : function() {
this.channel('sys').on('tick', this._onTick, this);
},
'' : function() {
this.channel('sys').un('tick', this._onTick, this);
}
}
},
_onTick: function(){
var y = ++this._curFrame * this._size;
(y >= this._size * 36) && (this._curFrame = y = 0);
this.elem('icon').css(this._bgProp, this._posPrefix + y +'px');
},
destruct : function() {
this.channel('sys').un('tick', this._onTick, this);
this.__base.apply(this, arguments);
}
});After:
modules.define(
'i-bem__dom',
['tick'],
function(provide, tick, BEMDOM) {
var FRAME_COUNT = 36;
BEMDOM.decl('b-spin', {
onSetMod : {
'js' : {
'inited' : function() { // constructor
var hasBackgroundPositionY = !!this.elem('icon').css('background-position-y')); /* In IE we can't get the background-position property, only background-position-y */
this._bgProp = hasBackgroundPositionY? 'background-position-y' : 'background-position';
this._posPrefix = hasBackgroundPositionY? '-' : '0 -';
this._curFrame = 0;
this._size = Number(this.getMod('size') || /[\d]+/.exec(this.getMod('theme'))[0]);
this.hasMod('progress') && this._bindToTick();
},
'' : function() { // destructor
this._unbindFromTick();
}
},
'progress' : {
'true' : function() {
this._bindToTick();
},
'' : function() {
this._unbindFromTick();
}
}
},
_bindToTick : function() {
tick.on('tick', this._onTick, this);
},
_unbindFromTick : function() {
tick.un('tick', this._onTick, this);
},
_onTick : function() {
var offset;
this._curFrame++ >= FRAME_COUNT?
offset = this._curFrame * this._size :
this._curFrame = offset = 0;
this.elem('icon').css(this._bgProp, this._posPrefix + offset + 'px');
}
});
provide(BEMDOM);
});