UNPKG

bem.js

Version:

DOM selection and manipulation using BEM (Block, Element, Modifier).

186 lines (159 loc) 6.98 kB
/* * This module provides BEM (Block Element Modifier) related methods * These methods can be used as an abstraction to talk to the DOM * BEM is a CSS methodology separating blocks (block) from elements (__element) and modifiers (--modifier) * BEM examples: alert, alert--warning, form__button, form__button--disabled * @see https://en.bem.info/methodology/key-concepts/ * @module */ /** * BEM class * Contains static methods with BEM abstraction to DOM manipulation * @class */ class BEM { /** * Get a node by BEM (Block Element Modifier) description * @param {string} block The outer block or component * @param {string} [element] An optional element within the outer block * @param {string} [modifier] An optional modifier or (e.g. state or theme) for a block/element * @returns {HTMLElement} */ static getBEMNode(block, element, modifier) { let selector = `.${BEM.getBEMClassName(block, element, modifier)}`; return document.querySelector(selector); } /** * Get multiple nodes by BEM (Block Element Modifier) description * @param {string} block The outer block or component * @param {string} [element] An optional element within the outer block * @param {string} [modifier] An optional modifier or (e.g. state or theme) for a block/element * @returns {NodeList} */ static getBEMNodes(block, element, modifier) { let selector = `.${BEM.getBEMClassName(block, element, modifier)}`; return document.querySelectorAll(selector); } /** * Get a child node by BEM (Block Element Modifier) description * @param {HTMLElement} node The parent node * @param {string} block The outer block or component * @param {string} [element] An optional element within the outer block * @param {string} [modifier] An optional modifier or (e.g. state or theme) for a block/element * @returns {HTMLElement} */ static getChildBEMNode(node, block, element, modifier) { let selector = `.${BEM.getBEMClassName(block, element, modifier)}`; return node.querySelector(selector); } /** * Get a child node by BEM (Block Element Modifier) description * @param {HTMLElement} node The parent node * @param {string} block The outer block or component * @param {string} [element] An optional element within the outer block * @param {string} [modifier] An optional modifier or (e.g. state or theme) for a block/element * @returns {HTMLElement} */ static getChildBEMNodes(node, block, element, modifier) { let selector = `.${BEM.getBEMClassName(block, element, modifier)}`; return node.querySelectorAll(selector); } /** * Get a BEM (Block Element Modifier) (CSS) selector * @param {string} block The outer block or component * @param {string} [element] An optional element within the outer block * @param {string} [modifier] An optional modifier or (e.g. state or theme) for a block/element * @returns {string} */ static getBEMSelector(block, element, modifier) { let selector = '.' + BEM.getBEMClassName(block, element); if (modifier) { selector += '.' + BEM.getBEMClassName(block, element, modifier); } return selector; } /** * Get a BEM (Block Element Modifier) class name * @param {string} block The outer block or component * @param {string} [element] An optional element within the outer block * @param {string} [modifier] An optional modifier or (e.g. state or theme) for a block/element * @returns {string} */ static getBEMClassName(block, element, modifier) { let className = block; if (element) { className += `__${element}`; } if (modifier) { className += `--${modifier}`; } return className; } /** * Add an additional class name with a specific modifier (--modifier) to a BEM (Block Element Modifier) element * A modifier class is created for each of the existing class names * Class names containing "--" (modifier pattern) are discarded * Double class names are prevented * @param {HTMLElement} node The block/element to append the class name to (block, block__element) * @param {string} modifier The name of the modifier (--name) * @param {boolean} [exp=true] Optional: If true: add the modifier */ static addModifier(node, modifier, exp=true) { if (!exp) { return; } [].forEach.call(node.classList, classListItem => { // Discard class names containing "--" (modifier pattern) if (classListItem.match('--')) { return; } let modifierClassName = `${classListItem}--${modifier}`; // Prevent double class names if (node.classList.contains(modifierClassName)) { return; } node.classList.add(modifierClassName); }); } /** * Remove all class names with a specific modifier (--modifier) from a BEM (Block Element Modifier) element * @param {HTMLElement} node The block/element to remove the class names from (block, block__element) * @param {string} modifier The name of the modifier (--name) * @param {boolean} [exp=true] Optional: If true: remove the modifier */ static removeModifier(node, modifier, exp=true) { if (!exp) { return; } let regex = new RegExp(`--${modifier}$`, 'g'); let classNames = [].filter.call(node.classList, className => !className.match(regex)); node.className = classNames.join(' '); } /** * Toggles between addModifier() and removeModifier() based on the presence of modifier (--modifier) * Block/element names are NOT taken into account while matching * @param {HTMLElement} node The block/element to remove the class names from (block, block__element) * @param {string} modifier The name of the modifier (--name) * @param {boolean} [exp] Optional: If true: add the modifier, if false: remove the modifier */ static toggleModifier(node, modifier, exp=!BEM.hasModifier(node, modifier)) { if (exp) { BEM.addModifier(node, modifier); return; } BEM.removeModifier(node, modifier); } /** * Returns whether node has modifier (--modifier) * Block/element names are NOT taken into account while matching * @param {HTMLElement} node The block/element to check * @param {string} modifier The name of the modifier (--name) * @returns {boolean} */ static hasModifier(node, modifier) { let regex = new RegExp(`--${modifier}(?=\\s|$)`, 'g'); // Regex matching specific modifier return node.className.match(regex); } } export default BEM; export { BEM };