Skip to main content

BEM(Block-Element-Modifier)

Free2016-08-06#Front-End#Tool#js BEM#JavaScript Block-Element-Modifier#js 修饰器#BEM与Yeoman

What is BEM, what is it used for, how to use it

##I. BEM Introduction

BEM claims to be a Frontend Development Methodology, providing a set of methods for development phase including naming rules, CSS/JS modularization principles, build tools, etc.

P.S. Emphasizing development phase is to distinguish from things like Yeoman, BEM wants to talk about methodology, not tools

Provides a set of naming rules (BEM naming rules), mainly used for modular CSS, but BEM is also used in JS and file naming and other aspects

Mainly solves these problems in CSS:

  • Style naming conflicts in team collaboration (such as class)

Solved with long class names, not using structured selectors (such as child selectors, descendant selectors, etc.), class names carry hierarchical relationships

  • Achieve the goal of self-documenting code

Semantic class names, provide more information, such as element name, function, component name, etc.

  • Avoid mutual influence between components

Don't rely on structured selectors, rely entirely on class names

  • How to assemble various features on the same DOM node while avoiding duplicate code (copy-paste)

Mix, such as div.classA>div.classB combines Block A and Block B

These solutions are explained in [How to Write Good CSS](/articles/如何写好 css/), if only defining a set of long class name formats, it's really meaningless, so BEM also implements frameworks and tools for this set of naming, ensuring it can be used well during development

##II. Terminology Concepts

(Suggest skipping, directly look at red parts, official explanation equals no explanation)

###Block

Page component with independent logic and page function, is a reusable unit, features as follows:

  • Can be freely nested and combined

  • Can be placed anywhere on any page, without affecting function and appearance

  • Reusable, interface can have any number of instances of the same Block

###Element

Part of Block, depends on Block existence (cannot be used outside Block)

###Modifier

[Optional] Defines appearance and behavior of Block and Element, just like HTML attributes, can make the same kind of Block look different

###BEM entity

All three above are

###Mix

An instance composed of various BEM entities on a single DOM node, features as follows:

  • Can combine behavior and style of several BEM entities, avoid code duplication

  • Semantically create new interface components based on existing BEM entities

###BEM tree

Use BEM to describe web page structure

###Block implementation

Many different technologies determine aspects of BEM entity's behavior, appearance, testing, templates, documentation, dependency description, extra data (such as images), etc.

###Implementation technology

Technology used to implement a Block, can be one or multiple

###Block redefinition

Modify Block implementation by adding new features to Block at different levels

###Redefinition level

A series of BEM entities and their partial implementations

Can be viewed as logical levels during use, such as project level, library level, former can rewrite latter's Block function and appearance

P.S. Actually implemented through file directory and sequential introduction for level isolation and rewriting

Points needing attention:

  • B(Block): Represents module, smallest reusable unit, functionally independent, can be nested, combined

  • E(Element): Part of B

  • M(Modifier): Represents E's state (E has different functions and appearance under different states), also part of B

  • BEM tree: Use BEM terminology to describe page/project structure

  • Block implementation: Things needed to implement Block, including all related content, such as JS, CSS, image, etc.

  • Redefinition level: Can be viewed as logical level, can rewrite/extend lower level Block (module) at higher level

##III. BEM Naming Rules

block-name__element-name_mod-name, for example nav-menu__item_active

block-name itself may not correspond to style, only serves as Block's logical name

BEM only provides general method, doesn't limit must use this naming rule

##IV. JavaScript for BEM

###Naming rules also apply to JS

In JS, Modifier is used to express logic of Block or Element (in CSS, Modifier is used to define appearance), JS describes behavior of Block and Element through a series of states

Don't find components through class, but through Block name (in HTML, not only uniquely identified through class, can also be tag, attribute, etc.), for example:

document.querySelector('.button')
  .addEventListener('click', function() {
    document.querySelector('.popup').classList.toggle('popup_visible');
}, false);

// BEM Style (pseudo code)
block('button').click(function() {
    block('popup').toggleMod('visible');
});

Practical application needs to introduce i-bem.js

Modifier can set specific state of Block, achieve Block state switching through adding and removing Modifier, operations on Modifier will trigger events, notify related Block. To ensure this mechanism runs correctly, should not allow随意 switching states or directly modifying class of some DOM node, these operations must be implemented through helpers provided by BEM (this restriction is similar to svn local repository)

Modifier corresponds to state, state associates function. When adding Modifier, state changes, automatically applies corresponding function; when removing Modifier, corresponding function will be automatically removed

Define association between Modifier and state and underlying level:

block('button').onSetMod({
    focused: {
        true: this.onFocus,
        '': this.onBlur
    }
});

Shields things under state (may be adding/removing Modifier causes state change, or triggered by user behavior), at programming level the minimum unit needing attention is state (Modifier). Can define appearance of various states by adding styles to Modifier, can also change or completely rewrite Block behavior through Redefinition level

###Separate code into various files, follow file directory structure rules

Rules:

  • Each Block's logic and optional Element and Modifier are placed in separate files

  • Each component's JS file directory structure follows BEM file directory structure rules

Example:

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

Facilitates management, reuse, transplantation, supports Redefinition level and combined use

###Separate logic into various Redefinition levels

Implement new Block at different Redefinition levels can inherit and extend existing Block, or completely rewrite the Block, for example:

// Completely rewrite
block('button').onSetMod({
    focused: {
        true: this.someCustomOnFocused
    }
});
// Partially rewrite
block('button').onSetMod({
    focused: {
        true: function() {
            this.__base.apply(this, arguments);
            this.someCustomOnFocused();
        }
    }
});

###Principles

Block in JS has no strong correspondence with DOM nodes, for example some Blocks have no DOM representation, just purely logical blocks

Interaction between Blocks is achieved through event subscription, for example:

  • Subscribe to events of other Block instances

  • Subscribe to Modifier changes

  • Directly call public interfaces of other Blocks

  • Any other interaction patterns, such as Event Channel (message mechanism)

Element can only be accessed through API provided by its belonging Block, cannot directly access Element in some Block

##V. File Directory Structure

Code is decomposed into independent small parts, facilitates developing independent Blocks, assemble before release for optimization

###Features

  • Decompose Block implementation into various separate files

Facilitates management (during development) and transplantation (implant into other projects)

  • Optional Element and Modifier are stored in various separate files

Only introduce related Block implementation (load on demand)

  • Files are grouped by semantics, not by file type

For example all Blocks are placed under block directory, to ensure only needed Blocks are introduced

  • Project is divided into various Redefinition levels

To eliminate duplicate code, facilitate rewriting/extending existing Library Block, when Library Block updates, won't affect upper level rewriting/extending

###Directory Structure Example

blocks/
    input/          # input block directory
        _type/                        # type modifier directory
            input_type_search.css     # Implementation of modifier type
                                      # with value search in CSS technology
        __box/                        # box element directory
            input__box.css
        input.css
        input.js
    button/         # button block directory
        button.css
        button.js
        button.png
    popup/          # popup block directory
        _target/
            popup_target.css           # Common code of  modifier target
            popup_target_anchor.css    # Modifier target with value anchor
            popup_target_position.css  # Modifier target with value position
        _visible/
            popup_visible.css          # Boolean modifier visible
    popup.css
    popup.js

File names also follow BEM naming rules, shared parts between Modifiers can be extracted, as separate file (popup_target.css)

###Redefinition level Example

library.blocks/
    button/
        button.css    # CSS implementation in the linked library (height 20px)
project.blocks/
    button/
        button.css    # Redefinition of CSS implementation (height 24px)

Files are stored at different levels, separated to avoid mutual influence

Or layer by platform:

common.blocks/
    button/
        button.css    # Generic CSS implementation of the button
desktop.blocks/
    button/
        button.css    # Desktop platform-specific button features
mobile.blocks/
    button/
        button.css    # Mobile platform-specific button features

build process will automatically introduce by level, for example:

@import(common.blocks/button/button.css);    /* Generic CSS rules */
@import(desktop.blocks/button/button.css);   /* Desktop platform-specific */

##VI. build

BEM projects all have multi-level file structure, build completes work:

  • Integrate separate files related to Block

  • Introduce Block, Element and Modifier on demand

  • Ensure introduction in correct order (guarantee Redefinition level)

Of course, need to pre-define some dependency information, such as:

  • List related Block, Element and Modifier when creating page

  • Indicate dependency relationships of Block, Element and Modifier in page

Dependency declaration can be completed through tools, such as can automatically generate BEM Tree according to class applied in HTML, or generate dependency information according to project structure (not on-demand generation, all things will be packaged in, suitable for situations where everything under project directory is determined to be necessary, such as class libraries), for more information about dependency declaration methods, please check Declarations in BEM

Dependency introduction scheme is related to implementation, for example, CSS uses @import, JS uses AMD and other schemes

###build result

// Before build
blocks/           # Directory containing the blocks
bundles/          # Directory containing all build results
  hello/          # Directory for the hello page
  hello.decl.js   # List of BEM entities required for the hello page
An example of a post-build file structure of a BEM project:

// After build
blocks/           # Directory containing the blocks
bundles/          # Directory containing all build results
  hello/          # Directory for the hello page
  hello.decl.js   # List of BEM entities required for the hello page
  hello.css       # Built CSS file for the hello page
  hello.js        # Built JavaScript file for thehello page

In above example hello.decl.js defines dependent components and their dependency relationships

###build tool

Official provided build tool is ENB, said to be very powerful:

The BEM platform uses ENB, which is a tool suitable for building BEM projects of any level of complexity.

Want to try please check beginner tutorial: Starting your own BEM project

###Reference Materials

Comments

No comments yet. Be the first to share your thoughts.

Leave a comment