In the BEM methodology, JavaScript is used for making a webpage “come alive” and is considered one of the block implementation technologies.
BEM enforces additional rules on JavaScript that help to apply all the concepts of the component approach of the BEM methodology.
JavaScript is one of the block implementation technologies, so the main concepts of the BEM methodology can be observed when working with JavaScript:
In web development, the final product (such as a webpage) consists of different technologies (HTML, CSS, JS, and so on). In BEM, working with all these technologies uses the same terminology and implementation approaches. This means that the entire team of a BEM project gets a unified language for communication, operating in terms of blocks, elements, and modifiers.
So the JavaScript implementation of blocks doesn’t use the concepts of DOM elements, but uses the next level of abstraction — the BEM tree. The advantage to this is we don’t rely on classes, and can independently describe the behavior of blocks and their optional elements. In JavaScript, modifiers are used for expressing the logic of a block or element (similar to CSS, where modifiers are used for defining appearance). The behavior of blocks and elements is described in JavaScript as a set of states.
Using the same concepts in all the technologies means we can implement various helpers in JavaScript for working with components and avoid hard coding the names of blocks and separators. This approach allows us to, for example, find all the elements with a particular name within the scope of a block, set a modifier for them, and check its value.
Example
Let’s take the example of a popup window (popup
).
There are several ways to show a popup window:
Use the common solution of adding the appropriate class. This method isn’t necessarily convenient, because you have to hard code the block name.
document.querySelector('.button')
.addEventListener('click', function() {
document.querySelector('.popup').classList.toggle('popup_visible');
}, false);
Use BEM principles and operate not in classes, but in blocks, elements, and modifiers.
In this case, a component is searched for not by the class, but by the name of the block, which can be identified in the project not only by a class, but also by a tag, attribute, and so on. Displaying the popup window (switching the popup
block to the visible
state) is also performed using a modifier, not a class.
block('button').click(function() {
block('popup').toggleMod('visible');
});
Important! Examples written for the BEM methodology use pseudocode. Real implementation examples are provided in the documentation for i-bem.js .
Using a unified subject domain makes it possible to interact with components at a higher level.
Modifiers can set specific states for blocks. The block logic is implemented in JavaScript and described using states. A block can be switched to another state by setting or removing a modifier. The change to the modifier creates an event that can be used for working with the block.
For example, to select a checkbox, you need to set the checked
modifier to true
for the checkbox
block.
In a BEM project, you can’t change the states in runtime by using modifiers and directly changing the CSS class on the corresponding DOM node. For the JavaScript to work correctly, all actions with modifiers must be performed using helper methods.
For implementation examples, see the documentation for i-bem.js.
Switching a block from one state to another often causes changes in its appearance. If the CSS uses a modifier to define the block’s appearance, changing the block state called by the same modifier will automatically apply all the necessary styles.
In BEM, the response to setting or removing a modifier is described declaratively. So, for example, if an additional class (or modifier) appears in the CSS during execution, all of this modifier’s properties are automatically applied to the DOM node of this class. The same thing occurs in JavaScript: if a modifier appears (a new class is added to the DOM node), the entire functionality of this modifier is applied. If the modifier disappears, the functionality is disabled.
To dynamically change the states of blocks and elements, we use special methods for setting and removing modifiers.
For implementation examples, see the documentation for i-bem.js .
Example
Let’s look at a form for sending a message. The following condition should be met: if an invalid email is entered, the Send button (the button
block) is inactive (it gets the button_disabled
modifier).
We could hard code all the conditions and continuously check it. This approach is inconvenient because any change will require manual changes to the code.
We could declare the block behavior and get the ability to override each modifier separately on a new redefinition level. The declaration can specify which block or element should respond to modifier changes.
block('button').onSetMod({
focused: {
true: this.onFocus,
'': this.onBlur
}
});
This approach allows us to:
block('button').setMod('focused')
or the user set/removed the focus with the cursor).We can apply the main BEM principles for organizing and storing code to JavaScript:
Example
Let’s look at an example of a logo (the logo
block) implemented in two technologies: a template and styles.
HTML implementation of the block:
<a class="logo" href="/"> Your awesome company</a>
CSS implementation of the block:
.logo {
width: 150px;
height: 100px;
}
The logo
block in the project’s file system:
logo/
logo.css # Block's appearance
logo.tmpl # Templates for generating the block’s HTML representation
Adding JavaScript functionality to the logo
block: now clicking the logo causes an action. According to the BEM methodology, the new behavior of the logo
block will be implemented like this:
.js
extension.logo.js
file will be located in the block’s logo/
directory.JavaScript implementation of the block:
document.querySelector('.logo').addEventListener('click', doSomething, false);
The logo.js
file in the block’s file system:
logo/
logo.css # Block’s appearance
logo.tmpl # Templates for generating the block’s HTML representation
logo.js # Dynamic behavior of the block in the browser
Dividing the code into parts and strictly organizing the project’s file system not only make it easier to navigate the project and reuse or migrate components, but also allow us to work with redefinition levels for JavaScript and use assembly.
The documentation for the BEM methodology provides many examples where the final CSS implementation of a block is assembled from different redefinition levels. Applying BEM principles to JavaScript allows us to similarly divide a block’s behavior into different levels:
Use redefinition levels to create a generic JavaScript library of blocks and change it at the project level. Then use assembly and only include the necessary block behaviors in the project.
Example
Let’s return to the example of a form for sending a message:
block('button').onSetMod({
focused: {
true: this.onFocus,
'': this.onBlur
}
});
BEM style allows us to:
Completely override the block’s behavior on a different redefinition level.
block('button').onSetMod({
focused: {
true: this.someCustomOnFocused
}
});
Add or partially change the block’s behavior on a different redefinition level.
block('button').onSetMod({
focused: {
true: function() {
this.__base.apply(this, arguments);
this.someCustomOnFocused();
}
}
});
You can use a specialized framework for working with redefinition levels in BEM, such as i-bem.js, since it was created to meet BEM requirements.
The fastest way is to start applying the principles of the BEM methodology in your project and get your first results without using a specialized framework.
To immediately use all the BEM concepts in your project, you need to use the i-bem.js framework: