BEM helpers for jQuery
BEM (Block, Element, Modifier) is a popular front-end methodology that introduces unified semantic entities across different frontend technologies such as CSS, HTML, JavaScript and templates.
BEM is not tied to any particular implementation or framework. This jQuery plugin provides basic support for working with modifiers and setting up callbacks based on modifier change.
This plugin supports multiple BEM naming conventions.
By default, it uses BEM naming convention introduced by Yandex:
- CSS class names are used to denote BEM entities
- Blocks with and without prefix are supported
- Elements: block__element
- Boolean modifiers: block_mod, block__element_mod
- Key/value modifiers: block_modname_modval, block__element_modname_modval
You can set alternative naming conventions by calling the $.BEMsyntax
method:
// to read the existing syntax
var currentSyntax = $.BEMsyntax();
// to set a new syntax
var newSyntax = $.BEMsyntax({
elem: '__', // separator between block and element name
modBefore: '--', // separator between block/element and modifier name
modKeyVal: '_' // separator between modifier key and modifier value
});
You can omit some properties if you inherit them from a previously defined scheme or default Yandex scheme. Changes are effective immediately and persist until you change them again.
newSyntax
contains an object describing the new syntax (all fields).
.setMod
sets a block modifier on each element of a jQuery collection, and returns the same collection.
.setMod(blockName, modName, true|false)
Sets a boolean modifier modName
for each block blockName
in a provided jQuery collection.
Third argument is the modifier value: true
adds a modifier, false
removes it.
According to BEM, the DOM element that receives a modifier also needs the original block or element CSS class to be assigned (so, modifier classes are never used standalone). Currently, the library does not check or enforce this rule, but might start doing that in any future release.
Example usage:
// Add 'hidden' boolean modifier to the 'b-widget' block
$('.b-widget').setMod('b-widget', 'hidden', true);
// Remove 'hidden' boolean modifier from the 'b-widget' block
$('.b-widget').setMod('b-widget', 'hidden', false);
.setMod(blockName, elemName, modName, true|false)
Same as above, but the second argument is an element name.
Key/value modifiers are different from boolean modifiers in that they have distinct string values. Values follow the same limitations as modifier names do; namely, they cannot contain underscores and should comply with general limitations of CSS class names.
.setMod(blockName, modName, modVal)
modVal should not be a boolean value, otherwise it will be treated as a boolean modifier.
If you also set modVal to an empty String
''
, the modifier will be removed.
Example usage:
// Add 'theme' key/value modifier to the 'b-widget' block
$('.b-widget').setMod('b-widget', 'theme', 'light');
// Remove 'theme' key/value modifier from the 'b-widget' block
$('.b-widget').setMod('b-widget', 'theme', '');
.setMod(blockName, elemName, modName, modVal)
Same as above, but the second argument is an element name. modVal should not also be a boolean value.
This is a simple API to read modifier values. It only reads the first element in a jQuery collection, ignoring all the rest, and returns a primitive value based on what it finds.
For blocks:
.getMod(blockName, modName)
For elements:
.getMod(blockName, elemName, modName)
It returns boolean true
for a boolean modifier that exists, a String
modifier value for a key/value modifier that exists, and boolean false
for a modifier that was not found (as it is not there, there is no way to determine whether we're dealing with a boolean or key/value modifier).
For an empty jQuery collection, the method would return undefined
. This might be an inconsistency; I may change it later.
.hasMod
checks whether the first item in a jQuery collection has a modifier set on a specified block/element.
Returns a boolean value. For key/value modifiers, return true
if any modifier value is present, regardless of the actual value.
For blocks:
var hasMod = $('.b-block').hasMod('b-block', 'expanded');
For elements:
var hasMod = $('.b-block__elem').hasMod('b-block', 'elem', 'expanded');
For each modifier being set, a jQuery setMod
event is being triggered that propagates up the DOM tree. It also contains metadata that lets you get additional information. This allows for callbacks being assigned to modifier changes.
For each modifier being set, a custom jQuery event fires which unique name is formed according to a pattern that is described below. The event also contains metadata you can read from the second argument of an event handler.
For all modifiers (both boolean and key/value pairs), the custom event is formed like this:
For blocks: setMod:block_modName_*
For elements: setMod:block__elem_modName_*
NOTE: If you define a custom BEM syntax with $.BEMsyntax()
method, you should adjust your event name patterns accordingly. Events being triggered always use the current syntax.
$(document).on('setMod:widget_init_*', function(e, data) {
console.log('Block name:', data.block);
console.log('Element name:', data.elem); // undefined, as it's a block-level modifier
console.log('Modifier name:', data.modName);
console.log('Modifier value:', data.modVal);
});
$('div.widget').setMod('widget', 'init', true);
/* Console output:
> Block name: widget
> Element name: undefined
> Modifier name: init
> Modifier value: true
*/
An asterisk *
at the end of an event name means it's triggered for all modifier values. An object passed as a second argument to an event handler contains additional keys:
block
— block nameelem
— element name (undefined
if modifier is set on a block level)modName
— modifier namemodVal
— modifier value that is set: a booleantrue
/false
for boolean modifiers, aString
value for key/value modifiers
TIP: For key/value modifier only, an additional event is triggered which is specific to a modifier value being set. Its custom name is formed like this:
For blocks: setMod:block_modName_modVal
For elements: setMod:block__elem_modName_modVal
$(document).on('setMod:widget_theme_light', function(e, data) {
console.log('Block name:', data.block);
console.log('Element name:', data.elem); // undefined, as it's a block-level modifier
console.log('Modifier name:', data.modName);
console.log('Modifier value:', data.modVal);
});
$('div.widget').setMod('widget', 'theme', 'light');
/* Console output:
> Block name: widget
> Element name: undefined
> Modifier name: theme
> Modifier value: light
*/
So as an example if you set setMod:widget_theme_light
event listener, then it will be triggered only if you set the theme
key/value modifier's value to light
. Any other values won't trigger the event listener.
NOTE: Please also note that if you have removed the string modifier value by setting it to an empty String
, $('div.widget').setMod('widget', 'theme', '');
then the specific event listener won't again be triggered obviously! In such cases you might consider setting the wild-card event listener, just like how you set it for boolean modifiers: setMod:widget_theme_*
.