Skip to content

Latest commit

History

62 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

TAO.BEM

tao-bem - базовая ООП библиотека, соответвующая методологии БЭМ. Представляет собой описание блоков в виде js-классов и инкапсуляции необходимого состояния в объектах этих классов.

БЭМ (Блок, Элемент, Модификатор) - опенсорс-технология разработки сайтов, реализующая принцип разделения интерфейса на независимые блоки с возможностью многкратного их использования.

Выделение абстракций - Блоков, Элементов и Модификаторов позволяет легко обрабатывать события, добавлять модификаторы и выполнять множество других опрераций над блоком и его элементами.

Установка

Для установки пакета применяем команду yarn.

yarn add @webtechart/tao-bem

Подключение и использование

Для описания минимального рабочего блока необхоидимо создать отдельный класс, описывающий этот блок и наследуемый от базового класса TAO.BEM. В этом классе обязательно переопределить статический геттер blockName(), который должен возвращать css-класс данного блока.

Далее необходимо провести инициализацию созданного класса в системе tao-bem, вызвав в нем статический метод register().

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}}News.register()

В примере выше мы создали и зарегистрировали самый простой блок. Правда, абсолютно бесполезный. Попробуем добавить немного логики в наш блок. Это может быть реализация метода onInit, подписка на события и реализация их обработчиков, добавление различных кастомных методов.

Ниже приведен пример расширения реализации класса блока.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}onInit(){this.debug='is_init';console.log('Instance of News initialized!');}staticgetevents(){return{click: function(event){this.makeSomeNoise();event.stopPropagation();},mouseout: 'makeSomeNoise'};}staticgetmodsEvents(){return{onMod: {'test-mod': function(modValue,modName){this.makeSomeNoise(modValue);}},onElemMod: {'button' : {'disabled': function(modValue,modName){this.makeSomeNoise();}}}};}staticgetelementsEvents(){return{'button.click': function(element,allElements,event){window.location.reload();}};}makeSomeNoise(noiseLevel=0){alert('Noise!'+(noiseLevel ? ' Going to noise level '+noiseLevel : ''));}}News.register();

Переопределив метод onInit(), мы получим возможность произвести необходимую подготовку в тот момент, когда TAO.BEM обнаруживает в DOM-дереве элемент блока и обрабатывает связанные с ним скрипты. Пока для примера оставим простое сообщение в консоли.

Переопределив статический геттер events(), мы подписываемся на события вызываемые на блоке. В данном случае мы вызываем метод makeSomeNoise при клике по блоку и выводе курсора с блока.

Переопределив статический геттер modsEvents(), мы добавили обработчик на изменение модификатора test-mod самого блока и изменении модификатора disabled у элемента button этого блока (т.е. на переключение класс b-news__button--disabled у элемента с классом b-news__button).

Переопределив статический геттер elementsEvents(), мы добавили обработчик на клик по элементу button (класс b-news__button).

Инициализация блоков

При подключении TAO.BEM скрипт обходит имеющееся DOM-дерево, отыскивает DOM-элементы с классами зарегистрированных блоков (тех, чьи блоки возвращают из статического геттера blockName), и для каждого создаются экземпляры соотвествующих классов BEM-блоков.

Также скрипт подписывается на изменения DOM-дерева, и при последующих добавлениях новых DOM-элементов для них также при необходимости будут созданы экземпляры BEM-блоков.

При необходимости стартовая автоматическая инициализация блоков может быть заменена "ленивой" инициализацией при первом появлении блока на экране. Для этого нужно переопределить статический геттер forced() у класса блока и вернуть false.

Организация структуры блоков

После инициализации ссылки на все созданные блоки попадают в Регистр. Если есть ссылка на DOM-элемент, который представляет блок, то из Регистра можно получить соответствующий ему BEM-объект с помощью метода getInstance($node, name).

importBEMfrom'tao-bem';let$node=$('.b-news');letNews=BEM.Registry.getInstance($node,'b-news');News.makeSomeNoise();

Для удобства манипулирования наборами блоков введено поятие Коллеций.

Коллеция - это специальный объект, который позволяет хранить несколько блоков и выполнять над ними групповые операции.

Объект коллекции наследует коллекцию из TAO.Core, поэтому в нём доступны все методы родителя. Помимо этого существуют несколько специфичных методов, которые позволяют изменять модификаторы всем элементам коллекции (addMod(name, state), delMod(name, state), toggleMod(name, state)) и метод byMod(name, state) для фильтрации элементов коллекции по значению модификатора.

Для получения коллекции блоков необходимо воспользоваться Регистром - методом getCollection(name). Или воспользоваться статическим методом getCollection() класса требуемого блока.

importBEMfrom'tao-bem';// ниже два примера идентичного кодаletcollection=BEM.Registry.getCollection('b-news');collection.first().makeSomeNoise();letsameCollection=News.getCollection();sameCollection.first().makeSomeNoise();

У каждого блока с помощью метода elems(name) можно получить коллекцию его элементов, и первый из элементов с помощью метода elem(name).

importBEMfrom'tao-bem';let$node=$('.b-news');letnewsBlock=BEM.Registry.getInstance($node,'b-news');letbuttons=newsBlock.elems();letfirstButton=newsBlock.elem();buttons.delMod('disabled');firstButton.addMod('disabled');

Для блоков есть возможность переопределения класса коллекции, чтобы расширить его дополнительными методами. Для этого нужно переопределить статический метод makeCollection() класса блока. Из него вернуть новый объект коллекции, который будет использоваться для хранения блоков.

Обработка событий блока

Во время жизни блока, скорее всего, ему придётся как-то взаимодействовать с окружающим его кодом и реагировать на действия пользователей. Такие взаимодействия удобно организовывать в виде событий. Для обработки событий блока в tao-bem существует несколько статических геттеров:

events()

Используется для подписки на события самого блока. Этот метод должен вернуть хэш-таблицу (обычный объект JS), ключами которой являются имена событий, на которые мы хотим подписатсья, а значениями - обработчики. В качестве обработчика может выступать как привычная всем анонимная функция (можно использовать и стрелочные функции, но в них будет потерян контекст this, а значит придётся явно получать объект блока, на котором произошло событие), так и строка, содержащая имя метода класса блока.

modsEvents()

Используется для подписки на события изменений значений модификаторов блока. Удаление модификатора обрабатывается как событие изменения значения модификатора на false, а добавление модификатора без значения обрабатывается как событие изменения значения модификатора на true.

В обработчик будет передано новое значение - модификатор и имя модификатора. Текущим контекстом будет класс блока.

Важно понимать, что обработка событий будет происходить только при изменении модификаторов через api TAO.BEM, т.е. вызовами методов addMod, delMod, toggleMod и mod. Также обработчик однократно сработает при инициализации блока для всех модификаторов, которые присутствуют на DOM-элементе.

Вернув из обработчика false, можно отменить изменение модификатора (однако обработчик вызванный на событии при инициализации блока не сможет таким образом "сбросить" модификаторы).

Помимо общих обработчиков на изменение модификаторов возможно подписываться на присвоение некоторым модификаторам определённых значений. В этом случае обработчик будет срабатывать только если модификатор получает подходящее значение. Ниже приведён пример.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}staticgetmodsEvents(){return{onMod: {'some-mod': {'some': function(modValue,modName){this.makeSomeNoise(modValue);},'someOther': function(modValue,modName){this.makeSomeNoise(modValue);}}}};}makeSomeNoise(noiseLevel=0){alert('Noise!'+(noiseLevel ? ' Going to noise level '+noiseLevel : ''));}}News.register()

Также можно навешивать обработчики на изменение модификаторов элементов блока. Работает это аналогично добавлению обаботчиков на изменение модиикаторов блока, только ключом будет не onMod, а onElemMod. Ниже пример.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}staticgetmodsEvents(){return{onElemMod: {'button': {'disabled': function(modValue,modName){this.makeSomeNoise(modValue);},}}};}makeSomeNoise(noiseLevel=0){alert('Noise!'+(noiseLevel ? ' Going to noise level '+noiseLevel : ''));}}News.register()

Автомодификаторы

Для блока можно включить автоматическое переключение определённых модификаторов при наступлении определённых событий блока. На текущий момент поддерживаются следующие события:

  • hover - наведение курсора мыши
  • press - клик
  • focus - получение фокуса

Для включения автомодификаторов нужно переопределить статический геттер mods() и вернуть из него строку, содержащую список необходимых модификаторов, разделённых пробелом.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}staticgetmods(){return'hover press';}}News.register();

Взаимодействие между блоками

Как описывалось выше все блоки можно получить через регистр коллекций: BEM.Registry.getCollection('b-news'). Но тут есть небольшая тонкость.

Так как инициализация блоков происходит последовательно, то нет гарантиии, что в момент инициализации конкретного блока уже будут иметься в коллекции экземпляры других блоков. Поэтому внутри метода onInit не стоит надеяться найти BEM.Registкy коллекцию других блоков. Для решения данной проблемы существует 2 способа:

  1. Получить нужный блок через промис.
  2. Производить инициализацию только после создания экземпляров всех связанных блоков.

Оба способа работают только для единичных экземляров (появления первого инстанса), если требуется ожидать все экземляры блока, то придется придумывать что-то своё.

Получение блока через промис

Данный вариант подходит если нужно получить связанный блок однакратно для выполнения каких-то операций, никак не влияющих на поведение текущего блока.

Получить промис можно через регистр: BEM.Registry.waitBlock('block_name'), аргументом метода является строка с именем блока. Метод возвращает промис, который пока не поддерживает ошибок (будет ждать бесконечно), а в момент появления требуемого блока в регистре вызовет резолв промиса с параметром, являющимся объектом-экземляром данного блока. Пример:

BEM.Registry.waitBlock('b-gallery').then(gallery=>gallery.goto(0));

Ждем пока будет инициализирован блок b-gallery, после чего на нем вызываем метод goto

Ожидание связанных блоков

Тут все сложнее:

  • Для начала нужно указать классы всех связанных блоков через статический геттер relatedBlocks.
  • Внутри метода onInit произвести инициализацию только логики, не относящейся к связанным блокам. А остальную инициализацию проводить в методе onRelatedBlocksInit, который будет вызван только полсле того, как будут инициализированы все связанные блоки (или не будет вызван, если не будет нужных блоков - будьте осторожны с опечатками в именах).
  • После вызова onRelatedBlocksInit, как минимум, по одному экземляру для каждого из зависимых блоков будет находиться в регистре блоков. Но для упрощения доступа можно воспользоваться свойством relatedBlocks текущего блока, который содержит Set для первых найденных инстанцев для кадого из зависимых блоков.

Пример:

importBEMfrom'tao-bem'classGalleryextendsBEM.Block{staticgetblockName(){return'b-gallery';}staticgetrelatedBlocks(){return['b-gallery-controls','b-page'];}onRelatedBlocksInit(){this.relatedBlocks.get('b-page').initSlider();this.relatedBlocks.get('b-gallery-controls').setSlider(this);}}

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
GitHub - techart/tao-bem · GitHub
Skip to content

Latest commit

History

62 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

TAO.BEM

tao-bem - базовая ООП библиотека, соответвующая методологии БЭМ. Представляет собой описание блоков в виде js-классов и инкапсуляции необходимого состояния в объектах этих классов.

БЭМ (Блок, Элемент, Модификатор) - опенсорс-технология разработки сайтов, реализующая принцип разделения интерфейса на независимые блоки с возможностью многкратного их использования.

Выделение абстракций - Блоков, Элементов и Модификаторов позволяет легко обрабатывать события, добавлять модификаторы и выполнять множество других опрераций над блоком и его элементами.

Установка

Для установки пакета применяем команду yarn.

yarn add @webtechart/tao-bem

Подключение и использование

Для описания минимального рабочего блока необхоидимо создать отдельный класс, описывающий этот блок и наследуемый от базового класса TAO.BEM. В этом классе обязательно переопределить статический геттер blockName(), который должен возвращать css-класс данного блока.

Далее необходимо провести инициализацию созданного класса в системе tao-bem, вызвав в нем статический метод register().

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}}News.register()

В примере выше мы создали и зарегистрировали самый простой блок. Правда, абсолютно бесполезный. Попробуем добавить немного логики в наш блок. Это может быть реализация метода onInit, подписка на события и реализация их обработчиков, добавление различных кастомных методов.

Ниже приведен пример расширения реализации класса блока.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}onInit(){this.debug='is_init';console.log('Instance of News initialized!');}staticgetevents(){return{click: function(event){this.makeSomeNoise();event.stopPropagation();},mouseout: 'makeSomeNoise'};}staticgetmodsEvents(){return{onMod: {'test-mod': function(modValue,modName){this.makeSomeNoise(modValue);}},onElemMod: {'button' : {'disabled': function(modValue,modName){this.makeSomeNoise();}}}};}staticgetelementsEvents(){return{'button.click': function(element,allElements,event){window.location.reload();}};}makeSomeNoise(noiseLevel=0){alert('Noise!'+(noiseLevel ? ' Going to noise level '+noiseLevel : ''));}}News.register();

Переопределив метод onInit(), мы получим возможность произвести необходимую подготовку в тот момент, когда TAO.BEM обнаруживает в DOM-дереве элемент блока и обрабатывает связанные с ним скрипты. Пока для примера оставим простое сообщение в консоли.

Переопределив статический геттер events(), мы подписываемся на события вызываемые на блоке. В данном случае мы вызываем метод makeSomeNoise при клике по блоку и выводе курсора с блока.

Переопределив статический геттер modsEvents(), мы добавили обработчик на изменение модификатора test-mod самого блока и изменении модификатора disabled у элемента button этого блока (т.е. на переключение класс b-news__button--disabled у элемента с классом b-news__button).

Переопределив статический геттер elementsEvents(), мы добавили обработчик на клик по элементу button (класс b-news__button).

Инициализация блоков

При подключении TAO.BEM скрипт обходит имеющееся DOM-дерево, отыскивает DOM-элементы с классами зарегистрированных блоков (тех, чьи блоки возвращают из статического геттера blockName), и для каждого создаются экземпляры соотвествующих классов BEM-блоков.

Также скрипт подписывается на изменения DOM-дерева, и при последующих добавлениях новых DOM-элементов для них также при необходимости будут созданы экземпляры BEM-блоков.

При необходимости стартовая автоматическая инициализация блоков может быть заменена "ленивой" инициализацией при первом появлении блока на экране. Для этого нужно переопределить статический геттер forced() у класса блока и вернуть false.

Организация структуры блоков

После инициализации ссылки на все созданные блоки попадают в Регистр. Если есть ссылка на DOM-элемент, который представляет блок, то из Регистра можно получить соответствующий ему BEM-объект с помощью метода getInstance($node, name).

importBEMfrom'tao-bem';let$node=$('.b-news');letNews=BEM.Registry.getInstance($node,'b-news');News.makeSomeNoise();

Для удобства манипулирования наборами блоков введено поятие Коллеций.

Коллеция - это специальный объект, который позволяет хранить несколько блоков и выполнять над ними групповые операции.

Объект коллекции наследует коллекцию из TAO.Core, поэтому в нём доступны все методы родителя. Помимо этого существуют несколько специфичных методов, которые позволяют изменять модификаторы всем элементам коллекции (addMod(name, state), delMod(name, state), toggleMod(name, state)) и метод byMod(name, state) для фильтрации элементов коллекции по значению модификатора.

Для получения коллекции блоков необходимо воспользоваться Регистром - методом getCollection(name). Или воспользоваться статическим методом getCollection() класса требуемого блока.

importBEMfrom'tao-bem';// ниже два примера идентичного кодаletcollection=BEM.Registry.getCollection('b-news');collection.first().makeSomeNoise();letsameCollection=News.getCollection();sameCollection.first().makeSomeNoise();

У каждого блока с помощью метода elems(name) можно получить коллекцию его элементов, и первый из элементов с помощью метода elem(name).

importBEMfrom'tao-bem';let$node=$('.b-news');letnewsBlock=BEM.Registry.getInstance($node,'b-news');letbuttons=newsBlock.elems();letfirstButton=newsBlock.elem();buttons.delMod('disabled');firstButton.addMod('disabled');

Для блоков есть возможность переопределения класса коллекции, чтобы расширить его дополнительными методами. Для этого нужно переопределить статический метод makeCollection() класса блока. Из него вернуть новый объект коллекции, который будет использоваться для хранения блоков.

Обработка событий блока

Во время жизни блока, скорее всего, ему придётся как-то взаимодействовать с окружающим его кодом и реагировать на действия пользователей. Такие взаимодействия удобно организовывать в виде событий. Для обработки событий блока в tao-bem существует несколько статических геттеров:

events()

Используется для подписки на события самого блока. Этот метод должен вернуть хэш-таблицу (обычный объект JS), ключами которой являются имена событий, на которые мы хотим подписатсья, а значениями - обработчики. В качестве обработчика может выступать как привычная всем анонимная функция (можно использовать и стрелочные функции, но в них будет потерян контекст this, а значит придётся явно получать объект блока, на котором произошло событие), так и строка, содержащая имя метода класса блока.

modsEvents()

Используется для подписки на события изменений значений модификаторов блока. Удаление модификатора обрабатывается как событие изменения значения модификатора на false, а добавление модификатора без значения обрабатывается как событие изменения значения модификатора на true.

В обработчик будет передано новое значение - модификатор и имя модификатора. Текущим контекстом будет класс блока.

Важно понимать, что обработка событий будет происходить только при изменении модификаторов через api TAO.BEM, т.е. вызовами методов addMod, delMod, toggleMod и mod. Также обработчик однократно сработает при инициализации блока для всех модификаторов, которые присутствуют на DOM-элементе.

Вернув из обработчика false, можно отменить изменение модификатора (однако обработчик вызванный на событии при инициализации блока не сможет таким образом "сбросить" модификаторы).

Помимо общих обработчиков на изменение модификаторов возможно подписываться на присвоение некоторым модификаторам определённых значений. В этом случае обработчик будет срабатывать только если модификатор получает подходящее значение. Ниже приведён пример.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}staticgetmodsEvents(){return{onMod: {'some-mod': {'some': function(modValue,modName){this.makeSomeNoise(modValue);},'someOther': function(modValue,modName){this.makeSomeNoise(modValue);}}}};}makeSomeNoise(noiseLevel=0){alert('Noise!'+(noiseLevel ? ' Going to noise level '+noiseLevel : ''));}}News.register()

Также можно навешивать обработчики на изменение модификаторов элементов блока. Работает это аналогично добавлению обаботчиков на изменение модиикаторов блока, только ключом будет не onMod, а onElemMod. Ниже пример.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}staticgetmodsEvents(){return{onElemMod: {'button': {'disabled': function(modValue,modName){this.makeSomeNoise(modValue);},}}};}makeSomeNoise(noiseLevel=0){alert('Noise!'+(noiseLevel ? ' Going to noise level '+noiseLevel : ''));}}News.register()

Автомодификаторы

Для блока можно включить автоматическое переключение определённых модификаторов при наступлении определённых событий блока. На текущий момент поддерживаются следующие события:

  • hover - наведение курсора мыши
  • press - клик
  • focus - получение фокуса

Для включения автомодификаторов нужно переопределить статический геттер mods() и вернуть из него строку, содержащую список необходимых модификаторов, разделённых пробелом.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}staticgetmods(){return'hover press';}}News.register();

Взаимодействие между блоками

Как описывалось выше все блоки можно получить через регистр коллекций: BEM.Registry.getCollection('b-news'). Но тут есть небольшая тонкость.

Так как инициализация блоков происходит последовательно, то нет гарантиии, что в момент инициализации конкретного блока уже будут иметься в коллекции экземпляры других блоков. Поэтому внутри метода onInit не стоит надеяться найти BEM.Registкy коллекцию других блоков. Для решения данной проблемы существует 2 способа:

  1. Получить нужный блок через промис.
  2. Производить инициализацию только после создания экземпляров всех связанных блоков.

Оба способа работают только для единичных экземляров (появления первого инстанса), если требуется ожидать все экземляры блока, то придется придумывать что-то своё.

Получение блока через промис

Данный вариант подходит если нужно получить связанный блок однакратно для выполнения каких-то операций, никак не влияющих на поведение текущего блока.

Получить промис можно через регистр: BEM.Registry.waitBlock('block_name'), аргументом метода является строка с именем блока. Метод возвращает промис, который пока не поддерживает ошибок (будет ждать бесконечно), а в момент появления требуемого блока в регистре вызовет резолв промиса с параметром, являющимся объектом-экземляром данного блока. Пример:

BEM.Registry.waitBlock('b-gallery').then(gallery=>gallery.goto(0));

Ждем пока будет инициализирован блок b-gallery, после чего на нем вызываем метод goto

Ожидание связанных блоков

Тут все сложнее:

  • Для начала нужно указать классы всех связанных блоков через статический геттер relatedBlocks.
  • Внутри метода onInit произвести инициализацию только логики, не относящейся к связанным блокам. А остальную инициализацию проводить в методе onRelatedBlocksInit, который будет вызван только полсле того, как будут инициализированы все связанные блоки (или не будет вызван, если не будет нужных блоков - будьте осторожны с опечатками в именах).
  • После вызова onRelatedBlocksInit, как минимум, по одному экземляру для каждого из зависимых блоков будет находиться в регистре блоков. Но для упрощения доступа можно воспользоваться свойством relatedBlocks текущего блока, который содержит Set для первых найденных инстанцев для кадого из зависимых блоков.

Пример:

importBEMfrom'tao-bem'classGalleryextendsBEM.Block{staticgetblockName(){return'b-gallery';}staticgetrelatedBlocks(){return['b-gallery-controls','b-page'];}onRelatedBlocksInit(){this.relatedBlocks.get('b-page').initSlider();this.relatedBlocks.get('b-gallery-controls').setSlider(this);}}

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - techart/tao-bem · GitHub
Skip to content

Latest commit

History

62 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

TAO.BEM

tao-bem - базовая ООП библиотека, соответвующая методологии БЭМ. Представляет собой описание блоков в виде js-классов и инкапсуляции необходимого состояния в объектах этих классов.

БЭМ (Блок, Элемент, Модификатор) - опенсорс-технология разработки сайтов, реализующая принцип разделения интерфейса на независимые блоки с возможностью многкратного их использования.

Выделение абстракций - Блоков, Элементов и Модификаторов позволяет легко обрабатывать события, добавлять модификаторы и выполнять множество других опрераций над блоком и его элементами.

Установка

Для установки пакета применяем команду yarn.

yarn add @webtechart/tao-bem

Подключение и использование

Для описания минимального рабочего блока необхоидимо создать отдельный класс, описывающий этот блок и наследуемый от базового класса TAO.BEM. В этом классе обязательно переопределить статический геттер blockName(), который должен возвращать css-класс данного блока.

Далее необходимо провести инициализацию созданного класса в системе tao-bem, вызвав в нем статический метод register().

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}}News.register()

В примере выше мы создали и зарегистрировали самый простой блок. Правда, абсолютно бесполезный. Попробуем добавить немного логики в наш блок. Это может быть реализация метода onInit, подписка на события и реализация их обработчиков, добавление различных кастомных методов.

Ниже приведен пример расширения реализации класса блока.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}onInit(){this.debug='is_init';console.log('Instance of News initialized!');}staticgetevents(){return{click: function(event){this.makeSomeNoise();event.stopPropagation();},mouseout: 'makeSomeNoise'};}staticgetmodsEvents(){return{onMod: {'test-mod': function(modValue,modName){this.makeSomeNoise(modValue);}},onElemMod: {'button' : {'disabled': function(modValue,modName){this.makeSomeNoise();}}}};}staticgetelementsEvents(){return{'button.click': function(element,allElements,event){window.location.reload();}};}makeSomeNoise(noiseLevel=0){alert('Noise!'+(noiseLevel ? ' Going to noise level '+noiseLevel : ''));}}News.register();

Переопределив метод onInit(), мы получим возможность произвести необходимую подготовку в тот момент, когда TAO.BEM обнаруживает в DOM-дереве элемент блока и обрабатывает связанные с ним скрипты. Пока для примера оставим простое сообщение в консоли.

Переопределив статический геттер events(), мы подписываемся на события вызываемые на блоке. В данном случае мы вызываем метод makeSomeNoise при клике по блоку и выводе курсора с блока.

Переопределив статический геттер modsEvents(), мы добавили обработчик на изменение модификатора test-mod самого блока и изменении модификатора disabled у элемента button этого блока (т.е. на переключение класс b-news__button--disabled у элемента с классом b-news__button).

Переопределив статический геттер elementsEvents(), мы добавили обработчик на клик по элементу button (класс b-news__button).

Инициализация блоков

При подключении TAO.BEM скрипт обходит имеющееся DOM-дерево, отыскивает DOM-элементы с классами зарегистрированных блоков (тех, чьи блоки возвращают из статического геттера blockName), и для каждого создаются экземпляры соотвествующих классов BEM-блоков.

Также скрипт подписывается на изменения DOM-дерева, и при последующих добавлениях новых DOM-элементов для них также при необходимости будут созданы экземпляры BEM-блоков.

При необходимости стартовая автоматическая инициализация блоков может быть заменена "ленивой" инициализацией при первом появлении блока на экране. Для этого нужно переопределить статический геттер forced() у класса блока и вернуть false.

Организация структуры блоков

После инициализации ссылки на все созданные блоки попадают в Регистр. Если есть ссылка на DOM-элемент, который представляет блок, то из Регистра можно получить соответствующий ему BEM-объект с помощью метода getInstance($node, name).

importBEMfrom'tao-bem';let$node=$('.b-news');letNews=BEM.Registry.getInstance($node,'b-news');News.makeSomeNoise();

Для удобства манипулирования наборами блоков введено поятие Коллеций.

Коллеция - это специальный объект, который позволяет хранить несколько блоков и выполнять над ними групповые операции.

Объект коллекции наследует коллекцию из TAO.Core, поэтому в нём доступны все методы родителя. Помимо этого существуют несколько специфичных методов, которые позволяют изменять модификаторы всем элементам коллекции (addMod(name, state), delMod(name, state), toggleMod(name, state)) и метод byMod(name, state) для фильтрации элементов коллекции по значению модификатора.

Для получения коллекции блоков необходимо воспользоваться Регистром - методом getCollection(name). Или воспользоваться статическим методом getCollection() класса требуемого блока.

importBEMfrom'tao-bem';// ниже два примера идентичного кодаletcollection=BEM.Registry.getCollection('b-news');collection.first().makeSomeNoise();letsameCollection=News.getCollection();sameCollection.first().makeSomeNoise();

У каждого блока с помощью метода elems(name) можно получить коллекцию его элементов, и первый из элементов с помощью метода elem(name).

importBEMfrom'tao-bem';let$node=$('.b-news');letnewsBlock=BEM.Registry.getInstance($node,'b-news');letbuttons=newsBlock.elems();letfirstButton=newsBlock.elem();buttons.delMod('disabled');firstButton.addMod('disabled');

Для блоков есть возможность переопределения класса коллекции, чтобы расширить его дополнительными методами. Для этого нужно переопределить статический метод makeCollection() класса блока. Из него вернуть новый объект коллекции, который будет использоваться для хранения блоков.

Обработка событий блока

Во время жизни блока, скорее всего, ему придётся как-то взаимодействовать с окружающим его кодом и реагировать на действия пользователей. Такие взаимодействия удобно организовывать в виде событий. Для обработки событий блока в tao-bem существует несколько статических геттеров:

events()

Используется для подписки на события самого блока. Этот метод должен вернуть хэш-таблицу (обычный объект JS), ключами которой являются имена событий, на которые мы хотим подписатсья, а значениями - обработчики. В качестве обработчика может выступать как привычная всем анонимная функция (можно использовать и стрелочные функции, но в них будет потерян контекст this, а значит придётся явно получать объект блока, на котором произошло событие), так и строка, содержащая имя метода класса блока.

modsEvents()

Используется для подписки на события изменений значений модификаторов блока. Удаление модификатора обрабатывается как событие изменения значения модификатора на false, а добавление модификатора без значения обрабатывается как событие изменения значения модификатора на true.

В обработчик будет передано новое значение - модификатор и имя модификатора. Текущим контекстом будет класс блока.

Важно понимать, что обработка событий будет происходить только при изменении модификаторов через api TAO.BEM, т.е. вызовами методов addMod, delMod, toggleMod и mod. Также обработчик однократно сработает при инициализации блока для всех модификаторов, которые присутствуют на DOM-элементе.

Вернув из обработчика false, можно отменить изменение модификатора (однако обработчик вызванный на событии при инициализации блока не сможет таким образом "сбросить" модификаторы).

Помимо общих обработчиков на изменение модификаторов возможно подписываться на присвоение некоторым модификаторам определённых значений. В этом случае обработчик будет срабатывать только если модификатор получает подходящее значение. Ниже приведён пример.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}staticgetmodsEvents(){return{onMod: {'some-mod': {'some': function(modValue,modName){this.makeSomeNoise(modValue);},'someOther': function(modValue,modName){this.makeSomeNoise(modValue);}}}};}makeSomeNoise(noiseLevel=0){alert('Noise!'+(noiseLevel ? ' Going to noise level '+noiseLevel : ''));}}News.register()

Также можно навешивать обработчики на изменение модификаторов элементов блока. Работает это аналогично добавлению обаботчиков на изменение модиикаторов блока, только ключом будет не onMod, а onElemMod. Ниже пример.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}staticgetmodsEvents(){return{onElemMod: {'button': {'disabled': function(modValue,modName){this.makeSomeNoise(modValue);},}}};}makeSomeNoise(noiseLevel=0){alert('Noise!'+(noiseLevel ? ' Going to noise level '+noiseLevel : ''));}}News.register()

Автомодификаторы

Для блока можно включить автоматическое переключение определённых модификаторов при наступлении определённых событий блока. На текущий момент поддерживаются следующие события:

  • hover - наведение курсора мыши
  • press - клик
  • focus - получение фокуса

Для включения автомодификаторов нужно переопределить статический геттер mods() и вернуть из него строку, содержащую список необходимых модификаторов, разделённых пробелом.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}staticgetmods(){return'hover press';}}News.register();

Взаимодействие между блоками

Как описывалось выше все блоки можно получить через регистр коллекций: BEM.Registry.getCollection('b-news'). Но тут есть небольшая тонкость.

Так как инициализация блоков происходит последовательно, то нет гарантиии, что в момент инициализации конкретного блока уже будут иметься в коллекции экземпляры других блоков. Поэтому внутри метода onInit не стоит надеяться найти BEM.Registкy коллекцию других блоков. Для решения данной проблемы существует 2 способа:

  1. Получить нужный блок через промис.
  2. Производить инициализацию только после создания экземпляров всех связанных блоков.

Оба способа работают только для единичных экземляров (появления первого инстанса), если требуется ожидать все экземляры блока, то придется придумывать что-то своё.

Получение блока через промис

Данный вариант подходит если нужно получить связанный блок однакратно для выполнения каких-то операций, никак не влияющих на поведение текущего блока.

Получить промис можно через регистр: BEM.Registry.waitBlock('block_name'), аргументом метода является строка с именем блока. Метод возвращает промис, который пока не поддерживает ошибок (будет ждать бесконечно), а в момент появления требуемого блока в регистре вызовет резолв промиса с параметром, являющимся объектом-экземляром данного блока. Пример:

BEM.Registry.waitBlock('b-gallery').then(gallery=>gallery.goto(0));

Ждем пока будет инициализирован блок b-gallery, после чего на нем вызываем метод goto

Ожидание связанных блоков

Тут все сложнее:

  • Для начала нужно указать классы всех связанных блоков через статический геттер relatedBlocks.
  • Внутри метода onInit произвести инициализацию только логики, не относящейся к связанным блокам. А остальную инициализацию проводить в методе onRelatedBlocksInit, который будет вызван только полсле того, как будут инициализированы все связанные блоки (или не будет вызван, если не будет нужных блоков - будьте осторожны с опечатками в именах).
  • После вызова onRelatedBlocksInit, как минимум, по одному экземляру для каждого из зависимых блоков будет находиться в регистре блоков. Но для упрощения доступа можно воспользоваться свойством relatedBlocks текущего блока, который содержит Set для первых найденных инстанцев для кадого из зависимых блоков.

Пример:

importBEMfrom'tao-bem'classGalleryextendsBEM.Block{staticgetblockName(){return'b-gallery';}staticgetrelatedBlocks(){return['b-gallery-controls','b-page'];}onRelatedBlocksInit(){this.relatedBlocks.get('b-page').initSlider();this.relatedBlocks.get('b-gallery-controls').setSlider(this);}}

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - techart/tao-bem · GitHub
Skip to content

Latest commit

History

62 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

TAO.BEM

tao-bem - базовая ООП библиотека, соответвующая методологии БЭМ. Представляет собой описание блоков в виде js-классов и инкапсуляции необходимого состояния в объектах этих классов.

БЭМ (Блок, Элемент, Модификатор) - опенсорс-технология разработки сайтов, реализующая принцип разделения интерфейса на независимые блоки с возможностью многкратного их использования.

Выделение абстракций - Блоков, Элементов и Модификаторов позволяет легко обрабатывать события, добавлять модификаторы и выполнять множество других опрераций над блоком и его элементами.

Установка

Для установки пакета применяем команду yarn.

yarn add @webtechart/tao-bem

Подключение и использование

Для описания минимального рабочего блока необхоидимо создать отдельный класс, описывающий этот блок и наследуемый от базового класса TAO.BEM. В этом классе обязательно переопределить статический геттер blockName(), который должен возвращать css-класс данного блока.

Далее необходимо провести инициализацию созданного класса в системе tao-bem, вызвав в нем статический метод register().

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}}News.register()

В примере выше мы создали и зарегистрировали самый простой блок. Правда, абсолютно бесполезный. Попробуем добавить немного логики в наш блок. Это может быть реализация метода onInit, подписка на события и реализация их обработчиков, добавление различных кастомных методов.

Ниже приведен пример расширения реализации класса блока.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}onInit(){this.debug='is_init';console.log('Instance of News initialized!');}staticgetevents(){return{click: function(event){this.makeSomeNoise();event.stopPropagation();},mouseout: 'makeSomeNoise'};}staticgetmodsEvents(){return{onMod: {'test-mod': function(modValue,modName){this.makeSomeNoise(modValue);}},onElemMod: {'button' : {'disabled': function(modValue,modName){this.makeSomeNoise();}}}};}staticgetelementsEvents(){return{'button.click': function(element,allElements,event){window.location.reload();}};}makeSomeNoise(noiseLevel=0){alert('Noise!'+(noiseLevel ? ' Going to noise level '+noiseLevel : ''));}}News.register();

Переопределив метод onInit(), мы получим возможность произвести необходимую подготовку в тот момент, когда TAO.BEM обнаруживает в DOM-дереве элемент блока и обрабатывает связанные с ним скрипты. Пока для примера оставим простое сообщение в консоли.

Переопределив статический геттер events(), мы подписываемся на события вызываемые на блоке. В данном случае мы вызываем метод makeSomeNoise при клике по блоку и выводе курсора с блока.

Переопределив статический геттер modsEvents(), мы добавили обработчик на изменение модификатора test-mod самого блока и изменении модификатора disabled у элемента button этого блока (т.е. на переключение класс b-news__button--disabled у элемента с классом b-news__button).

Переопределив статический геттер elementsEvents(), мы добавили обработчик на клик по элементу button (класс b-news__button).

Инициализация блоков

При подключении TAO.BEM скрипт обходит имеющееся DOM-дерево, отыскивает DOM-элементы с классами зарегистрированных блоков (тех, чьи блоки возвращают из статического геттера blockName), и для каждого создаются экземпляры соотвествующих классов BEM-блоков.

Также скрипт подписывается на изменения DOM-дерева, и при последующих добавлениях новых DOM-элементов для них также при необходимости будут созданы экземпляры BEM-блоков.

При необходимости стартовая автоматическая инициализация блоков может быть заменена "ленивой" инициализацией при первом появлении блока на экране. Для этого нужно переопределить статический геттер forced() у класса блока и вернуть false.

Организация структуры блоков

После инициализации ссылки на все созданные блоки попадают в Регистр. Если есть ссылка на DOM-элемент, который представляет блок, то из Регистра можно получить соответствующий ему BEM-объект с помощью метода getInstance($node, name).

importBEMfrom'tao-bem';let$node=$('.b-news');letNews=BEM.Registry.getInstance($node,'b-news');News.makeSomeNoise();

Для удобства манипулирования наборами блоков введено поятие Коллеций.

Коллеция - это специальный объект, который позволяет хранить несколько блоков и выполнять над ними групповые операции.

Объект коллекции наследует коллекцию из TAO.Core, поэтому в нём доступны все методы родителя. Помимо этого существуют несколько специфичных методов, которые позволяют изменять модификаторы всем элементам коллекции (addMod(name, state), delMod(name, state), toggleMod(name, state)) и метод byMod(name, state) для фильтрации элементов коллекции по значению модификатора.

Для получения коллекции блоков необходимо воспользоваться Регистром - методом getCollection(name). Или воспользоваться статическим методом getCollection() класса требуемого блока.

importBEMfrom'tao-bem';// ниже два примера идентичного кодаletcollection=BEM.Registry.getCollection('b-news');collection.first().makeSomeNoise();letsameCollection=News.getCollection();sameCollection.first().makeSomeNoise();

У каждого блока с помощью метода elems(name) можно получить коллекцию его элементов, и первый из элементов с помощью метода elem(name).

importBEMfrom'tao-bem';let$node=$('.b-news');letnewsBlock=BEM.Registry.getInstance($node,'b-news');letbuttons=newsBlock.elems();letfirstButton=newsBlock.elem();buttons.delMod('disabled');firstButton.addMod('disabled');

Для блоков есть возможность переопределения класса коллекции, чтобы расширить его дополнительными методами. Для этого нужно переопределить статический метод makeCollection() класса блока. Из него вернуть новый объект коллекции, который будет использоваться для хранения блоков.

Обработка событий блока

Во время жизни блока, скорее всего, ему придётся как-то взаимодействовать с окружающим его кодом и реагировать на действия пользователей. Такие взаимодействия удобно организовывать в виде событий. Для обработки событий блока в tao-bem существует несколько статических геттеров:

events()

Используется для подписки на события самого блока. Этот метод должен вернуть хэш-таблицу (обычный объект JS), ключами которой являются имена событий, на которые мы хотим подписатсья, а значениями - обработчики. В качестве обработчика может выступать как привычная всем анонимная функция (можно использовать и стрелочные функции, но в них будет потерян контекст this, а значит придётся явно получать объект блока, на котором произошло событие), так и строка, содержащая имя метода класса блока.

modsEvents()

Используется для подписки на события изменений значений модификаторов блока. Удаление модификатора обрабатывается как событие изменения значения модификатора на false, а добавление модификатора без значения обрабатывается как событие изменения значения модификатора на true.

В обработчик будет передано новое значение - модификатор и имя модификатора. Текущим контекстом будет класс блока.

Важно понимать, что обработка событий будет происходить только при изменении модификаторов через api TAO.BEM, т.е. вызовами методов addMod, delMod, toggleMod и mod. Также обработчик однократно сработает при инициализации блока для всех модификаторов, которые присутствуют на DOM-элементе.

Вернув из обработчика false, можно отменить изменение модификатора (однако обработчик вызванный на событии при инициализации блока не сможет таким образом "сбросить" модификаторы).

Помимо общих обработчиков на изменение модификаторов возможно подписываться на присвоение некоторым модификаторам определённых значений. В этом случае обработчик будет срабатывать только если модификатор получает подходящее значение. Ниже приведён пример.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}staticgetmodsEvents(){return{onMod: {'some-mod': {'some': function(modValue,modName){this.makeSomeNoise(modValue);},'someOther': function(modValue,modName){this.makeSomeNoise(modValue);}}}};}makeSomeNoise(noiseLevel=0){alert('Noise!'+(noiseLevel ? ' Going to noise level '+noiseLevel : ''));}}News.register()

Также можно навешивать обработчики на изменение модификаторов элементов блока. Работает это аналогично добавлению обаботчиков на изменение модиикаторов блока, только ключом будет не onMod, а onElemMod. Ниже пример.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}staticgetmodsEvents(){return{onElemMod: {'button': {'disabled': function(modValue,modName){this.makeSomeNoise(modValue);},}}};}makeSomeNoise(noiseLevel=0){alert('Noise!'+(noiseLevel ? ' Going to noise level '+noiseLevel : ''));}}News.register()

Автомодификаторы

Для блока можно включить автоматическое переключение определённых модификаторов при наступлении определённых событий блока. На текущий момент поддерживаются следующие события:

  • hover - наведение курсора мыши
  • press - клик
  • focus - получение фокуса

Для включения автомодификаторов нужно переопределить статический геттер mods() и вернуть из него строку, содержащую список необходимых модификаторов, разделённых пробелом.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}staticgetmods(){return'hover press';}}News.register();

Взаимодействие между блоками

Как описывалось выше все блоки можно получить через регистр коллекций: BEM.Registry.getCollection('b-news'). Но тут есть небольшая тонкость.

Так как инициализация блоков происходит последовательно, то нет гарантиии, что в момент инициализации конкретного блока уже будут иметься в коллекции экземпляры других блоков. Поэтому внутри метода onInit не стоит надеяться найти BEM.Registкy коллекцию других блоков. Для решения данной проблемы существует 2 способа:

  1. Получить нужный блок через промис.
  2. Производить инициализацию только после создания экземпляров всех связанных блоков.

Оба способа работают только для единичных экземляров (появления первого инстанса), если требуется ожидать все экземляры блока, то придется придумывать что-то своё.

Получение блока через промис

Данный вариант подходит если нужно получить связанный блок однакратно для выполнения каких-то операций, никак не влияющих на поведение текущего блока.

Получить промис можно через регистр: BEM.Registry.waitBlock('block_name'), аргументом метода является строка с именем блока. Метод возвращает промис, который пока не поддерживает ошибок (будет ждать бесконечно), а в момент появления требуемого блока в регистре вызовет резолв промиса с параметром, являющимся объектом-экземляром данного блока. Пример:

BEM.Registry.waitBlock('b-gallery').then(gallery=>gallery.goto(0));

Ждем пока будет инициализирован блок b-gallery, после чего на нем вызываем метод goto

Ожидание связанных блоков

Тут все сложнее:

  • Для начала нужно указать классы всех связанных блоков через статический геттер relatedBlocks.
  • Внутри метода onInit произвести инициализацию только логики, не относящейся к связанным блокам. А остальную инициализацию проводить в методе onRelatedBlocksInit, который будет вызван только полсле того, как будут инициализированы все связанные блоки (или не будет вызван, если не будет нужных блоков - будьте осторожны с опечатками в именах).
  • После вызова onRelatedBlocksInit, как минимум, по одному экземляру для каждого из зависимых блоков будет находиться в регистре блоков. Но для упрощения доступа можно воспользоваться свойством relatedBlocks текущего блока, который содержит Set для первых найденных инстанцев для кадого из зависимых блоков.

Пример:

importBEMfrom'tao-bem'classGalleryextendsBEM.Block{staticgetblockName(){return'b-gallery';}staticgetrelatedBlocks(){return['b-gallery-controls','b-page'];}onRelatedBlocksInit(){this.relatedBlocks.get('b-page').initSlider();this.relatedBlocks.get('b-gallery-controls').setSlider(this);}}

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' GitHub - techart/tao-bem · GitHub
Skip to content

Latest commit

History

62 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

TAO.BEM

tao-bem - базовая ООП библиотека, соответвующая методологии БЭМ. Представляет собой описание блоков в виде js-классов и инкапсуляции необходимого состояния в объектах этих классов.

БЭМ (Блок, Элемент, Модификатор) - опенсорс-технология разработки сайтов, реализующая принцип разделения интерфейса на независимые блоки с возможностью многкратного их использования.

Выделение абстракций - Блоков, Элементов и Модификаторов позволяет легко обрабатывать события, добавлять модификаторы и выполнять множество других опрераций над блоком и его элементами.

Установка

Для установки пакета применяем команду yarn.

yarn add @webtechart/tao-bem

Подключение и использование

Для описания минимального рабочего блока необхоидимо создать отдельный класс, описывающий этот блок и наследуемый от базового класса TAO.BEM. В этом классе обязательно переопределить статический геттер blockName(), который должен возвращать css-класс данного блока.

Далее необходимо провести инициализацию созданного класса в системе tao-bem, вызвав в нем статический метод register().

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}}News.register()

В примере выше мы создали и зарегистрировали самый простой блок. Правда, абсолютно бесполезный. Попробуем добавить немного логики в наш блок. Это может быть реализация метода onInit, подписка на события и реализация их обработчиков, добавление различных кастомных методов.

Ниже приведен пример расширения реализации класса блока.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}onInit(){this.debug='is_init';console.log('Instance of News initialized!');}staticgetevents(){return{click: function(event){this.makeSomeNoise();event.stopPropagation();},mouseout: 'makeSomeNoise'};}staticgetmodsEvents(){return{onMod: {'test-mod': function(modValue,modName){this.makeSomeNoise(modValue);}},onElemMod: {'button' : {'disabled': function(modValue,modName){this.makeSomeNoise();}}}};}staticgetelementsEvents(){return{'button.click': function(element,allElements,event){window.location.reload();}};}makeSomeNoise(noiseLevel=0){alert('Noise!'+(noiseLevel ? ' Going to noise level '+noiseLevel : ''));}}News.register();

Переопределив метод onInit(), мы получим возможность произвести необходимую подготовку в тот момент, когда TAO.BEM обнаруживает в DOM-дереве элемент блока и обрабатывает связанные с ним скрипты. Пока для примера оставим простое сообщение в консоли.

Переопределив статический геттер events(), мы подписываемся на события вызываемые на блоке. В данном случае мы вызываем метод makeSomeNoise при клике по блоку и выводе курсора с блока.

Переопределив статический геттер modsEvents(), мы добавили обработчик на изменение модификатора test-mod самого блока и изменении модификатора disabled у элемента button этого блока (т.е. на переключение класс b-news__button--disabled у элемента с классом b-news__button).

Переопределив статический геттер elementsEvents(), мы добавили обработчик на клик по элементу button (класс b-news__button).

Инициализация блоков

При подключении TAO.BEM скрипт обходит имеющееся DOM-дерево, отыскивает DOM-элементы с классами зарегистрированных блоков (тех, чьи блоки возвращают из статического геттера blockName), и для каждого создаются экземпляры соотвествующих классов BEM-блоков.

Также скрипт подписывается на изменения DOM-дерева, и при последующих добавлениях новых DOM-элементов для них также при необходимости будут созданы экземпляры BEM-блоков.

При необходимости стартовая автоматическая инициализация блоков может быть заменена "ленивой" инициализацией при первом появлении блока на экране. Для этого нужно переопределить статический геттер forced() у класса блока и вернуть false.

Организация структуры блоков

После инициализации ссылки на все созданные блоки попадают в Регистр. Если есть ссылка на DOM-элемент, который представляет блок, то из Регистра можно получить соответствующий ему BEM-объект с помощью метода getInstance($node, name).

importBEMfrom'tao-bem';let$node=$('.b-news');letNews=BEM.Registry.getInstance($node,'b-news');News.makeSomeNoise();

Для удобства манипулирования наборами блоков введено поятие Коллеций.

Коллеция - это специальный объект, который позволяет хранить несколько блоков и выполнять над ними групповые операции.

Объект коллекции наследует коллекцию из TAO.Core, поэтому в нём доступны все методы родителя. Помимо этого существуют несколько специфичных методов, которые позволяют изменять модификаторы всем элементам коллекции (addMod(name, state), delMod(name, state), toggleMod(name, state)) и метод byMod(name, state) для фильтрации элементов коллекции по значению модификатора.

Для получения коллекции блоков необходимо воспользоваться Регистром - методом getCollection(name). Или воспользоваться статическим методом getCollection() класса требуемого блока.

importBEMfrom'tao-bem';// ниже два примера идентичного кодаletcollection=BEM.Registry.getCollection('b-news');collection.first().makeSomeNoise();letsameCollection=News.getCollection();sameCollection.first().makeSomeNoise();

У каждого блока с помощью метода elems(name) можно получить коллекцию его элементов, и первый из элементов с помощью метода elem(name).

importBEMfrom'tao-bem';let$node=$('.b-news');letnewsBlock=BEM.Registry.getInstance($node,'b-news');letbuttons=newsBlock.elems();letfirstButton=newsBlock.elem();buttons.delMod('disabled');firstButton.addMod('disabled');

Для блоков есть возможность переопределения класса коллекции, чтобы расширить его дополнительными методами. Для этого нужно переопределить статический метод makeCollection() класса блока. Из него вернуть новый объект коллекции, который будет использоваться для хранения блоков.

Обработка событий блока

Во время жизни блока, скорее всего, ему придётся как-то взаимодействовать с окружающим его кодом и реагировать на действия пользователей. Такие взаимодействия удобно организовывать в виде событий. Для обработки событий блока в tao-bem существует несколько статических геттеров:

events()

Используется для подписки на события самого блока. Этот метод должен вернуть хэш-таблицу (обычный объект JS), ключами которой являются имена событий, на которые мы хотим подписатсья, а значениями - обработчики. В качестве обработчика может выступать как привычная всем анонимная функция (можно использовать и стрелочные функции, но в них будет потерян контекст this, а значит придётся явно получать объект блока, на котором произошло событие), так и строка, содержащая имя метода класса блока.

modsEvents()

Используется для подписки на события изменений значений модификаторов блока. Удаление модификатора обрабатывается как событие изменения значения модификатора на false, а добавление модификатора без значения обрабатывается как событие изменения значения модификатора на true.

В обработчик будет передано новое значение - модификатор и имя модификатора. Текущим контекстом будет класс блока.

Важно понимать, что обработка событий будет происходить только при изменении модификаторов через api TAO.BEM, т.е. вызовами методов addMod, delMod, toggleMod и mod. Также обработчик однократно сработает при инициализации блока для всех модификаторов, которые присутствуют на DOM-элементе.

Вернув из обработчика false, можно отменить изменение модификатора (однако обработчик вызванный на событии при инициализации блока не сможет таким образом "сбросить" модификаторы).

Помимо общих обработчиков на изменение модификаторов возможно подписываться на присвоение некоторым модификаторам определённых значений. В этом случае обработчик будет срабатывать только если модификатор получает подходящее значение. Ниже приведён пример.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}staticgetmodsEvents(){return{onMod: {'some-mod': {'some': function(modValue,modName){this.makeSomeNoise(modValue);},'someOther': function(modValue,modName){this.makeSomeNoise(modValue);}}}};}makeSomeNoise(noiseLevel=0){alert('Noise!'+(noiseLevel ? ' Going to noise level '+noiseLevel : ''));}}News.register()

Также можно навешивать обработчики на изменение модификаторов элементов блока. Работает это аналогично добавлению обаботчиков на изменение модиикаторов блока, только ключом будет не onMod, а onElemMod. Ниже пример.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}staticgetmodsEvents(){return{onElemMod: {'button': {'disabled': function(modValue,modName){this.makeSomeNoise(modValue);},}}};}makeSomeNoise(noiseLevel=0){alert('Noise!'+(noiseLevel ? ' Going to noise level '+noiseLevel : ''));}}News.register()

Автомодификаторы

Для блока можно включить автоматическое переключение определённых модификаторов при наступлении определённых событий блока. На текущий момент поддерживаются следующие события:

  • hover - наведение курсора мыши
  • press - клик
  • focus - получение фокуса

Для включения автомодификаторов нужно переопределить статический геттер mods() и вернуть из него строку, содержащую список необходимых модификаторов, разделённых пробелом.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}staticgetmods(){return'hover press';}}News.register();

Взаимодействие между блоками

Как описывалось выше все блоки можно получить через регистр коллекций: BEM.Registry.getCollection('b-news'). Но тут есть небольшая тонкость.

Так как инициализация блоков происходит последовательно, то нет гарантиии, что в момент инициализации конкретного блока уже будут иметься в коллекции экземпляры других блоков. Поэтому внутри метода onInit не стоит надеяться найти BEM.Registкy коллекцию других блоков. Для решения данной проблемы существует 2 способа:

  1. Получить нужный блок через промис.
  2. Производить инициализацию только после создания экземпляров всех связанных блоков.

Оба способа работают только для единичных экземляров (появления первого инстанса), если требуется ожидать все экземляры блока, то придется придумывать что-то своё.

Получение блока через промис

Данный вариант подходит если нужно получить связанный блок однакратно для выполнения каких-то операций, никак не влияющих на поведение текущего блока.

Получить промис можно через регистр: BEM.Registry.waitBlock('block_name'), аргументом метода является строка с именем блока. Метод возвращает промис, который пока не поддерживает ошибок (будет ждать бесконечно), а в момент появления требуемого блока в регистре вызовет резолв промиса с параметром, являющимся объектом-экземляром данного блока. Пример:

BEM.Registry.waitBlock('b-gallery').then(gallery=>gallery.goto(0));

Ждем пока будет инициализирован блок b-gallery, после чего на нем вызываем метод goto

Ожидание связанных блоков

Тут все сложнее:

  • Для начала нужно указать классы всех связанных блоков через статический геттер relatedBlocks.
  • Внутри метода onInit произвести инициализацию только логики, не относящейся к связанным блокам. А остальную инициализацию проводить в методе onRelatedBlocksInit, который будет вызван только полсле того, как будут инициализированы все связанные блоки (или не будет вызван, если не будет нужных блоков - будьте осторожны с опечатками в именах).
  • После вызова onRelatedBlocksInit, как минимум, по одному экземляру для каждого из зависимых блоков будет находиться в регистре блоков. Но для упрощения доступа можно воспользоваться свойством relatedBlocks текущего блока, который содержит Set для первых найденных инстанцев для кадого из зависимых блоков.

Пример:

importBEMfrom'tao-bem'classGalleryextendsBEM.Block{staticgetblockName(){return'b-gallery';}staticgetrelatedBlocks(){return['b-gallery-controls','b-page'];}onRelatedBlocksInit(){this.relatedBlocks.get('b-page').initSlider();this.relatedBlocks.get('b-gallery-controls').setSlider(this);}}

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - techart/tao-bem · GitHub
Skip to content

Latest commit

History

62 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

TAO.BEM

tao-bem - базовая ООП библиотека, соответвующая методологии БЭМ. Представляет собой описание блоков в виде js-классов и инкапсуляции необходимого состояния в объектах этих классов.

БЭМ (Блок, Элемент, Модификатор) - опенсорс-технология разработки сайтов, реализующая принцип разделения интерфейса на независимые блоки с возможностью многкратного их использования.

Выделение абстракций - Блоков, Элементов и Модификаторов позволяет легко обрабатывать события, добавлять модификаторы и выполнять множество других опрераций над блоком и его элементами.

Установка

Для установки пакета применяем команду yarn.

yarn add @webtechart/tao-bem

Подключение и использование

Для описания минимального рабочего блока необхоидимо создать отдельный класс, описывающий этот блок и наследуемый от базового класса TAO.BEM. В этом классе обязательно переопределить статический геттер blockName(), который должен возвращать css-класс данного блока.

Далее необходимо провести инициализацию созданного класса в системе tao-bem, вызвав в нем статический метод register().

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}}News.register()

В примере выше мы создали и зарегистрировали самый простой блок. Правда, абсолютно бесполезный. Попробуем добавить немного логики в наш блок. Это может быть реализация метода onInit, подписка на события и реализация их обработчиков, добавление различных кастомных методов.

Ниже приведен пример расширения реализации класса блока.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}onInit(){this.debug='is_init';console.log('Instance of News initialized!');}staticgetevents(){return{click: function(event){this.makeSomeNoise();event.stopPropagation();},mouseout: 'makeSomeNoise'};}staticgetmodsEvents(){return{onMod: {'test-mod': function(modValue,modName){this.makeSomeNoise(modValue);}},onElemMod: {'button' : {'disabled': function(modValue,modName){this.makeSomeNoise();}}}};}staticgetelementsEvents(){return{'button.click': function(element,allElements,event){window.location.reload();}};}makeSomeNoise(noiseLevel=0){alert('Noise!'+(noiseLevel ? ' Going to noise level '+noiseLevel : ''));}}News.register();

Переопределив метод onInit(), мы получим возможность произвести необходимую подготовку в тот момент, когда TAO.BEM обнаруживает в DOM-дереве элемент блока и обрабатывает связанные с ним скрипты. Пока для примера оставим простое сообщение в консоли.

Переопределив статический геттер events(), мы подписываемся на события вызываемые на блоке. В данном случае мы вызываем метод makeSomeNoise при клике по блоку и выводе курсора с блока.

Переопределив статический геттер modsEvents(), мы добавили обработчик на изменение модификатора test-mod самого блока и изменении модификатора disabled у элемента button этого блока (т.е. на переключение класс b-news__button--disabled у элемента с классом b-news__button).

Переопределив статический геттер elementsEvents(), мы добавили обработчик на клик по элементу button (класс b-news__button).

Инициализация блоков

При подключении TAO.BEM скрипт обходит имеющееся DOM-дерево, отыскивает DOM-элементы с классами зарегистрированных блоков (тех, чьи блоки возвращают из статического геттера blockName), и для каждого создаются экземпляры соотвествующих классов BEM-блоков.

Также скрипт подписывается на изменения DOM-дерева, и при последующих добавлениях новых DOM-элементов для них также при необходимости будут созданы экземпляры BEM-блоков.

При необходимости стартовая автоматическая инициализация блоков может быть заменена "ленивой" инициализацией при первом появлении блока на экране. Для этого нужно переопределить статический геттер forced() у класса блока и вернуть false.

Организация структуры блоков

После инициализации ссылки на все созданные блоки попадают в Регистр. Если есть ссылка на DOM-элемент, который представляет блок, то из Регистра можно получить соответствующий ему BEM-объект с помощью метода getInstance($node, name).

importBEMfrom'tao-bem';let$node=$('.b-news');letNews=BEM.Registry.getInstance($node,'b-news');News.makeSomeNoise();

Для удобства манипулирования наборами блоков введено поятие Коллеций.

Коллеция - это специальный объект, который позволяет хранить несколько блоков и выполнять над ними групповые операции.

Объект коллекции наследует коллекцию из TAO.Core, поэтому в нём доступны все методы родителя. Помимо этого существуют несколько специфичных методов, которые позволяют изменять модификаторы всем элементам коллекции (addMod(name, state), delMod(name, state), toggleMod(name, state)) и метод byMod(name, state) для фильтрации элементов коллекции по значению модификатора.

Для получения коллекции блоков необходимо воспользоваться Регистром - методом getCollection(name). Или воспользоваться статическим методом getCollection() класса требуемого блока.

importBEMfrom'tao-bem';// ниже два примера идентичного кодаletcollection=BEM.Registry.getCollection('b-news');collection.first().makeSomeNoise();letsameCollection=News.getCollection();sameCollection.first().makeSomeNoise();

У каждого блока с помощью метода elems(name) можно получить коллекцию его элементов, и первый из элементов с помощью метода elem(name).

importBEMfrom'tao-bem';let$node=$('.b-news');letnewsBlock=BEM.Registry.getInstance($node,'b-news');letbuttons=newsBlock.elems();letfirstButton=newsBlock.elem();buttons.delMod('disabled');firstButton.addMod('disabled');

Для блоков есть возможность переопределения класса коллекции, чтобы расширить его дополнительными методами. Для этого нужно переопределить статический метод makeCollection() класса блока. Из него вернуть новый объект коллекции, который будет использоваться для хранения блоков.

Обработка событий блока

Во время жизни блока, скорее всего, ему придётся как-то взаимодействовать с окружающим его кодом и реагировать на действия пользователей. Такие взаимодействия удобно организовывать в виде событий. Для обработки событий блока в tao-bem существует несколько статических геттеров:

events()

Используется для подписки на события самого блока. Этот метод должен вернуть хэш-таблицу (обычный объект JS), ключами которой являются имена событий, на которые мы хотим подписатсья, а значениями - обработчики. В качестве обработчика может выступать как привычная всем анонимная функция (можно использовать и стрелочные функции, но в них будет потерян контекст this, а значит придётся явно получать объект блока, на котором произошло событие), так и строка, содержащая имя метода класса блока.

modsEvents()

Используется для подписки на события изменений значений модификаторов блока. Удаление модификатора обрабатывается как событие изменения значения модификатора на false, а добавление модификатора без значения обрабатывается как событие изменения значения модификатора на true.

В обработчик будет передано новое значение - модификатор и имя модификатора. Текущим контекстом будет класс блока.

Важно понимать, что обработка событий будет происходить только при изменении модификаторов через api TAO.BEM, т.е. вызовами методов addMod, delMod, toggleMod и mod. Также обработчик однократно сработает при инициализации блока для всех модификаторов, которые присутствуют на DOM-элементе.

Вернув из обработчика false, можно отменить изменение модификатора (однако обработчик вызванный на событии при инициализации блока не сможет таким образом "сбросить" модификаторы).

Помимо общих обработчиков на изменение модификаторов возможно подписываться на присвоение некоторым модификаторам определённых значений. В этом случае обработчик будет срабатывать только если модификатор получает подходящее значение. Ниже приведён пример.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}staticgetmodsEvents(){return{onMod: {'some-mod': {'some': function(modValue,modName){this.makeSomeNoise(modValue);},'someOther': function(modValue,modName){this.makeSomeNoise(modValue);}}}};}makeSomeNoise(noiseLevel=0){alert('Noise!'+(noiseLevel ? ' Going to noise level '+noiseLevel : ''));}}News.register()

Также можно навешивать обработчики на изменение модификаторов элементов блока. Работает это аналогично добавлению обаботчиков на изменение модиикаторов блока, только ключом будет не onMod, а onElemMod. Ниже пример.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}staticgetmodsEvents(){return{onElemMod: {'button': {'disabled': function(modValue,modName){this.makeSomeNoise(modValue);},}}};}makeSomeNoise(noiseLevel=0){alert('Noise!'+(noiseLevel ? ' Going to noise level '+noiseLevel : ''));}}News.register()

Автомодификаторы

Для блока можно включить автоматическое переключение определённых модификаторов при наступлении определённых событий блока. На текущий момент поддерживаются следующие события:

  • hover - наведение курсора мыши
  • press - клик
  • focus - получение фокуса

Для включения автомодификаторов нужно переопределить статический геттер mods() и вернуть из него строку, содержащую список необходимых модификаторов, разделённых пробелом.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}staticgetmods(){return'hover press';}}News.register();

Взаимодействие между блоками

Как описывалось выше все блоки можно получить через регистр коллекций: BEM.Registry.getCollection('b-news'). Но тут есть небольшая тонкость.

Так как инициализация блоков происходит последовательно, то нет гарантиии, что в момент инициализации конкретного блока уже будут иметься в коллекции экземпляры других блоков. Поэтому внутри метода onInit не стоит надеяться найти BEM.Registкy коллекцию других блоков. Для решения данной проблемы существует 2 способа:

  1. Получить нужный блок через промис.
  2. Производить инициализацию только после создания экземпляров всех связанных блоков.

Оба способа работают только для единичных экземляров (появления первого инстанса), если требуется ожидать все экземляры блока, то придется придумывать что-то своё.

Получение блока через промис

Данный вариант подходит если нужно получить связанный блок однакратно для выполнения каких-то операций, никак не влияющих на поведение текущего блока.

Получить промис можно через регистр: BEM.Registry.waitBlock('block_name'), аргументом метода является строка с именем блока. Метод возвращает промис, который пока не поддерживает ошибок (будет ждать бесконечно), а в момент появления требуемого блока в регистре вызовет резолв промиса с параметром, являющимся объектом-экземляром данного блока. Пример:

BEM.Registry.waitBlock('b-gallery').then(gallery=>gallery.goto(0));

Ждем пока будет инициализирован блок b-gallery, после чего на нем вызываем метод goto

Ожидание связанных блоков

Тут все сложнее:

  • Для начала нужно указать классы всех связанных блоков через статический геттер relatedBlocks.
  • Внутри метода onInit произвести инициализацию только логики, не относящейся к связанным блокам. А остальную инициализацию проводить в методе onRelatedBlocksInit, который будет вызван только полсле того, как будут инициализированы все связанные блоки (или не будет вызван, если не будет нужных блоков - будьте осторожны с опечатками в именах).
  • После вызова onRelatedBlocksInit, как минимум, по одному экземляру для каждого из зависимых блоков будет находиться в регистре блоков. Но для упрощения доступа можно воспользоваться свойством relatedBlocks текущего блока, который содержит Set для первых найденных инстанцев для кадого из зависимых блоков.

Пример:

importBEMfrom'tao-bem'classGalleryextendsBEM.Block{staticgetblockName(){return'b-gallery';}staticgetrelatedBlocks(){return['b-gallery-controls','b-page'];}onRelatedBlocksInit(){this.relatedBlocks.get('b-page').initSlider();this.relatedBlocks.get('b-gallery-controls').setSlider(this);}}

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - techart/tao-bem · GitHub
Skip to content

Latest commit

History

62 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

TAO.BEM

tao-bem - базовая ООП библиотека, соответвующая методологии БЭМ. Представляет собой описание блоков в виде js-классов и инкапсуляции необходимого состояния в объектах этих классов.

БЭМ (Блок, Элемент, Модификатор) - опенсорс-технология разработки сайтов, реализующая принцип разделения интерфейса на независимые блоки с возможностью многкратного их использования.

Выделение абстракций - Блоков, Элементов и Модификаторов позволяет легко обрабатывать события, добавлять модификаторы и выполнять множество других опрераций над блоком и его элементами.

Установка

Для установки пакета применяем команду yarn.

yarn add @webtechart/tao-bem

Подключение и использование

Для описания минимального рабочего блока необхоидимо создать отдельный класс, описывающий этот блок и наследуемый от базового класса TAO.BEM. В этом классе обязательно переопределить статический геттер blockName(), который должен возвращать css-класс данного блока.

Далее необходимо провести инициализацию созданного класса в системе tao-bem, вызвав в нем статический метод register().

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}}News.register()

В примере выше мы создали и зарегистрировали самый простой блок. Правда, абсолютно бесполезный. Попробуем добавить немного логики в наш блок. Это может быть реализация метода onInit, подписка на события и реализация их обработчиков, добавление различных кастомных методов.

Ниже приведен пример расширения реализации класса блока.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}onInit(){this.debug='is_init';console.log('Instance of News initialized!');}staticgetevents(){return{click: function(event){this.makeSomeNoise();event.stopPropagation();},mouseout: 'makeSomeNoise'};}staticgetmodsEvents(){return{onMod: {'test-mod': function(modValue,modName){this.makeSomeNoise(modValue);}},onElemMod: {'button' : {'disabled': function(modValue,modName){this.makeSomeNoise();}}}};}staticgetelementsEvents(){return{'button.click': function(element,allElements,event){window.location.reload();}};}makeSomeNoise(noiseLevel=0){alert('Noise!'+(noiseLevel ? ' Going to noise level '+noiseLevel : ''));}}News.register();

Переопределив метод onInit(), мы получим возможность произвести необходимую подготовку в тот момент, когда TAO.BEM обнаруживает в DOM-дереве элемент блока и обрабатывает связанные с ним скрипты. Пока для примера оставим простое сообщение в консоли.

Переопределив статический геттер events(), мы подписываемся на события вызываемые на блоке. В данном случае мы вызываем метод makeSomeNoise при клике по блоку и выводе курсора с блока.

Переопределив статический геттер modsEvents(), мы добавили обработчик на изменение модификатора test-mod самого блока и изменении модификатора disabled у элемента button этого блока (т.е. на переключение класс b-news__button--disabled у элемента с классом b-news__button).

Переопределив статический геттер elementsEvents(), мы добавили обработчик на клик по элементу button (класс b-news__button).

Инициализация блоков

При подключении TAO.BEM скрипт обходит имеющееся DOM-дерево, отыскивает DOM-элементы с классами зарегистрированных блоков (тех, чьи блоки возвращают из статического геттера blockName), и для каждого создаются экземпляры соотвествующих классов BEM-блоков.

Также скрипт подписывается на изменения DOM-дерева, и при последующих добавлениях новых DOM-элементов для них также при необходимости будут созданы экземпляры BEM-блоков.

При необходимости стартовая автоматическая инициализация блоков может быть заменена "ленивой" инициализацией при первом появлении блока на экране. Для этого нужно переопределить статический геттер forced() у класса блока и вернуть false.

Организация структуры блоков

После инициализации ссылки на все созданные блоки попадают в Регистр. Если есть ссылка на DOM-элемент, который представляет блок, то из Регистра можно получить соответствующий ему BEM-объект с помощью метода getInstance($node, name).

importBEMfrom'tao-bem';let$node=$('.b-news');letNews=BEM.Registry.getInstance($node,'b-news');News.makeSomeNoise();

Для удобства манипулирования наборами блоков введено поятие Коллеций.

Коллеция - это специальный объект, который позволяет хранить несколько блоков и выполнять над ними групповые операции.

Объект коллекции наследует коллекцию из TAO.Core, поэтому в нём доступны все методы родителя. Помимо этого существуют несколько специфичных методов, которые позволяют изменять модификаторы всем элементам коллекции (addMod(name, state), delMod(name, state), toggleMod(name, state)) и метод byMod(name, state) для фильтрации элементов коллекции по значению модификатора.

Для получения коллекции блоков необходимо воспользоваться Регистром - методом getCollection(name). Или воспользоваться статическим методом getCollection() класса требуемого блока.

importBEMfrom'tao-bem';// ниже два примера идентичного кодаletcollection=BEM.Registry.getCollection('b-news');collection.first().makeSomeNoise();letsameCollection=News.getCollection();sameCollection.first().makeSomeNoise();

У каждого блока с помощью метода elems(name) можно получить коллекцию его элементов, и первый из элементов с помощью метода elem(name).

importBEMfrom'tao-bem';let$node=$('.b-news');letnewsBlock=BEM.Registry.getInstance($node,'b-news');letbuttons=newsBlock.elems();letfirstButton=newsBlock.elem();buttons.delMod('disabled');firstButton.addMod('disabled');

Для блоков есть возможность переопределения класса коллекции, чтобы расширить его дополнительными методами. Для этого нужно переопределить статический метод makeCollection() класса блока. Из него вернуть новый объект коллекции, который будет использоваться для хранения блоков.

Обработка событий блока

Во время жизни блока, скорее всего, ему придётся как-то взаимодействовать с окружающим его кодом и реагировать на действия пользователей. Такие взаимодействия удобно организовывать в виде событий. Для обработки событий блока в tao-bem существует несколько статических геттеров:

events()

Используется для подписки на события самого блока. Этот метод должен вернуть хэш-таблицу (обычный объект JS), ключами которой являются имена событий, на которые мы хотим подписатсья, а значениями - обработчики. В качестве обработчика может выступать как привычная всем анонимная функция (можно использовать и стрелочные функции, но в них будет потерян контекст this, а значит придётся явно получать объект блока, на котором произошло событие), так и строка, содержащая имя метода класса блока.

modsEvents()

Используется для подписки на события изменений значений модификаторов блока. Удаление модификатора обрабатывается как событие изменения значения модификатора на false, а добавление модификатора без значения обрабатывается как событие изменения значения модификатора на true.

В обработчик будет передано новое значение - модификатор и имя модификатора. Текущим контекстом будет класс блока.

Важно понимать, что обработка событий будет происходить только при изменении модификаторов через api TAO.BEM, т.е. вызовами методов addMod, delMod, toggleMod и mod. Также обработчик однократно сработает при инициализации блока для всех модификаторов, которые присутствуют на DOM-элементе.

Вернув из обработчика false, можно отменить изменение модификатора (однако обработчик вызванный на событии при инициализации блока не сможет таким образом "сбросить" модификаторы).

Помимо общих обработчиков на изменение модификаторов возможно подписываться на присвоение некоторым модификаторам определённых значений. В этом случае обработчик будет срабатывать только если модификатор получает подходящее значение. Ниже приведён пример.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}staticgetmodsEvents(){return{onMod: {'some-mod': {'some': function(modValue,modName){this.makeSomeNoise(modValue);},'someOther': function(modValue,modName){this.makeSomeNoise(modValue);}}}};}makeSomeNoise(noiseLevel=0){alert('Noise!'+(noiseLevel ? ' Going to noise level '+noiseLevel : ''));}}News.register()

Также можно навешивать обработчики на изменение модификаторов элементов блока. Работает это аналогично добавлению обаботчиков на изменение модиикаторов блока, только ключом будет не onMod, а onElemMod. Ниже пример.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}staticgetmodsEvents(){return{onElemMod: {'button': {'disabled': function(modValue,modName){this.makeSomeNoise(modValue);},}}};}makeSomeNoise(noiseLevel=0){alert('Noise!'+(noiseLevel ? ' Going to noise level '+noiseLevel : ''));}}News.register()

Автомодификаторы

Для блока можно включить автоматическое переключение определённых модификаторов при наступлении определённых событий блока. На текущий момент поддерживаются следующие события:

  • hover - наведение курсора мыши
  • press - клик
  • focus - получение фокуса

Для включения автомодификаторов нужно переопределить статический геттер mods() и вернуть из него строку, содержащую список необходимых модификаторов, разделённых пробелом.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}staticgetmods(){return'hover press';}}News.register();

Взаимодействие между блоками

Как описывалось выше все блоки можно получить через регистр коллекций: BEM.Registry.getCollection('b-news'). Но тут есть небольшая тонкость.

Так как инициализация блоков происходит последовательно, то нет гарантиии, что в момент инициализации конкретного блока уже будут иметься в коллекции экземпляры других блоков. Поэтому внутри метода onInit не стоит надеяться найти BEM.Registкy коллекцию других блоков. Для решения данной проблемы существует 2 способа:

  1. Получить нужный блок через промис.
  2. Производить инициализацию только после создания экземпляров всех связанных блоков.

Оба способа работают только для единичных экземляров (появления первого инстанса), если требуется ожидать все экземляры блока, то придется придумывать что-то своё.

Получение блока через промис

Данный вариант подходит если нужно получить связанный блок однакратно для выполнения каких-то операций, никак не влияющих на поведение текущего блока.

Получить промис можно через регистр: BEM.Registry.waitBlock('block_name'), аргументом метода является строка с именем блока. Метод возвращает промис, который пока не поддерживает ошибок (будет ждать бесконечно), а в момент появления требуемого блока в регистре вызовет резолв промиса с параметром, являющимся объектом-экземляром данного блока. Пример:

BEM.Registry.waitBlock('b-gallery').then(gallery=>gallery.goto(0));

Ждем пока будет инициализирован блок b-gallery, после чего на нем вызываем метод goto

Ожидание связанных блоков

Тут все сложнее:

  • Для начала нужно указать классы всех связанных блоков через статический геттер relatedBlocks.
  • Внутри метода onInit произвести инициализацию только логики, не относящейся к связанным блокам. А остальную инициализацию проводить в методе onRelatedBlocksInit, который будет вызван только полсле того, как будут инициализированы все связанные блоки (или не будет вызван, если не будет нужных блоков - будьте осторожны с опечатками в именах).
  • После вызова onRelatedBlocksInit, как минимум, по одному экземляру для каждого из зависимых блоков будет находиться в регистре блоков. Но для упрощения доступа можно воспользоваться свойством relatedBlocks текущего блока, который содержит Set для первых найденных инстанцев для кадого из зависимых блоков.

Пример:

importBEMfrom'tao-bem'classGalleryextendsBEM.Block{staticgetblockName(){return'b-gallery';}staticgetrelatedBlocks(){return['b-gallery-controls','b-page'];}onRelatedBlocksInit(){this.relatedBlocks.get('b-page').initSlider();this.relatedBlocks.get('b-gallery-controls').setSlider(this);}}

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); GitHub - techart/tao-bem · GitHub
Skip to content

Latest commit

History

62 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

TAO.BEM

tao-bem - базовая ООП библиотека, соответвующая методологии БЭМ. Представляет собой описание блоков в виде js-классов и инкапсуляции необходимого состояния в объектах этих классов.

БЭМ (Блок, Элемент, Модификатор) - опенсорс-технология разработки сайтов, реализующая принцип разделения интерфейса на независимые блоки с возможностью многкратного их использования.

Выделение абстракций - Блоков, Элементов и Модификаторов позволяет легко обрабатывать события, добавлять модификаторы и выполнять множество других опрераций над блоком и его элементами.

Установка

Для установки пакета применяем команду yarn.

yarn add @webtechart/tao-bem

Подключение и использование

Для описания минимального рабочего блока необхоидимо создать отдельный класс, описывающий этот блок и наследуемый от базового класса TAO.BEM. В этом классе обязательно переопределить статический геттер blockName(), который должен возвращать css-класс данного блока.

Далее необходимо провести инициализацию созданного класса в системе tao-bem, вызвав в нем статический метод register().

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}}News.register()

В примере выше мы создали и зарегистрировали самый простой блок. Правда, абсолютно бесполезный. Попробуем добавить немного логики в наш блок. Это может быть реализация метода onInit, подписка на события и реализация их обработчиков, добавление различных кастомных методов.

Ниже приведен пример расширения реализации класса блока.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}onInit(){this.debug='is_init';console.log('Instance of News initialized!');}staticgetevents(){return{click: function(event){this.makeSomeNoise();event.stopPropagation();},mouseout: 'makeSomeNoise'};}staticgetmodsEvents(){return{onMod: {'test-mod': function(modValue,modName){this.makeSomeNoise(modValue);}},onElemMod: {'button' : {'disabled': function(modValue,modName){this.makeSomeNoise();}}}};}staticgetelementsEvents(){return{'button.click': function(element,allElements,event){window.location.reload();}};}makeSomeNoise(noiseLevel=0){alert('Noise!'+(noiseLevel ? ' Going to noise level '+noiseLevel : ''));}}News.register();

Переопределив метод onInit(), мы получим возможность произвести необходимую подготовку в тот момент, когда TAO.BEM обнаруживает в DOM-дереве элемент блока и обрабатывает связанные с ним скрипты. Пока для примера оставим простое сообщение в консоли.

Переопределив статический геттер events(), мы подписываемся на события вызываемые на блоке. В данном случае мы вызываем метод makeSomeNoise при клике по блоку и выводе курсора с блока.

Переопределив статический геттер modsEvents(), мы добавили обработчик на изменение модификатора test-mod самого блока и изменении модификатора disabled у элемента button этого блока (т.е. на переключение класс b-news__button--disabled у элемента с классом b-news__button).

Переопределив статический геттер elementsEvents(), мы добавили обработчик на клик по элементу button (класс b-news__button).

Инициализация блоков

При подключении TAO.BEM скрипт обходит имеющееся DOM-дерево, отыскивает DOM-элементы с классами зарегистрированных блоков (тех, чьи блоки возвращают из статического геттера blockName), и для каждого создаются экземпляры соотвествующих классов BEM-блоков.

Также скрипт подписывается на изменения DOM-дерева, и при последующих добавлениях новых DOM-элементов для них также при необходимости будут созданы экземпляры BEM-блоков.

При необходимости стартовая автоматическая инициализация блоков может быть заменена "ленивой" инициализацией при первом появлении блока на экране. Для этого нужно переопределить статический геттер forced() у класса блока и вернуть false.

Организация структуры блоков

После инициализации ссылки на все созданные блоки попадают в Регистр. Если есть ссылка на DOM-элемент, который представляет блок, то из Регистра можно получить соответствующий ему BEM-объект с помощью метода getInstance($node, name).

importBEMfrom'tao-bem';let$node=$('.b-news');letNews=BEM.Registry.getInstance($node,'b-news');News.makeSomeNoise();

Для удобства манипулирования наборами блоков введено поятие Коллеций.

Коллеция - это специальный объект, который позволяет хранить несколько блоков и выполнять над ними групповые операции.

Объект коллекции наследует коллекцию из TAO.Core, поэтому в нём доступны все методы родителя. Помимо этого существуют несколько специфичных методов, которые позволяют изменять модификаторы всем элементам коллекции (addMod(name, state), delMod(name, state), toggleMod(name, state)) и метод byMod(name, state) для фильтрации элементов коллекции по значению модификатора.

Для получения коллекции блоков необходимо воспользоваться Регистром - методом getCollection(name). Или воспользоваться статическим методом getCollection() класса требуемого блока.

importBEMfrom'tao-bem';// ниже два примера идентичного кодаletcollection=BEM.Registry.getCollection('b-news');collection.first().makeSomeNoise();letsameCollection=News.getCollection();sameCollection.first().makeSomeNoise();

У каждого блока с помощью метода elems(name) можно получить коллекцию его элементов, и первый из элементов с помощью метода elem(name).

importBEMfrom'tao-bem';let$node=$('.b-news');letnewsBlock=BEM.Registry.getInstance($node,'b-news');letbuttons=newsBlock.elems();letfirstButton=newsBlock.elem();buttons.delMod('disabled');firstButton.addMod('disabled');

Для блоков есть возможность переопределения класса коллекции, чтобы расширить его дополнительными методами. Для этого нужно переопределить статический метод makeCollection() класса блока. Из него вернуть новый объект коллекции, который будет использоваться для хранения блоков.

Обработка событий блока

Во время жизни блока, скорее всего, ему придётся как-то взаимодействовать с окружающим его кодом и реагировать на действия пользователей. Такие взаимодействия удобно организовывать в виде событий. Для обработки событий блока в tao-bem существует несколько статических геттеров:

events()

Используется для подписки на события самого блока. Этот метод должен вернуть хэш-таблицу (обычный объект JS), ключами которой являются имена событий, на которые мы хотим подписатсья, а значениями - обработчики. В качестве обработчика может выступать как привычная всем анонимная функция (можно использовать и стрелочные функции, но в них будет потерян контекст this, а значит придётся явно получать объект блока, на котором произошло событие), так и строка, содержащая имя метода класса блока.

modsEvents()

Используется для подписки на события изменений значений модификаторов блока. Удаление модификатора обрабатывается как событие изменения значения модификатора на false, а добавление модификатора без значения обрабатывается как событие изменения значения модификатора на true.

В обработчик будет передано новое значение - модификатор и имя модификатора. Текущим контекстом будет класс блока.

Важно понимать, что обработка событий будет происходить только при изменении модификаторов через api TAO.BEM, т.е. вызовами методов addMod, delMod, toggleMod и mod. Также обработчик однократно сработает при инициализации блока для всех модификаторов, которые присутствуют на DOM-элементе.

Вернув из обработчика false, можно отменить изменение модификатора (однако обработчик вызванный на событии при инициализации блока не сможет таким образом "сбросить" модификаторы).

Помимо общих обработчиков на изменение модификаторов возможно подписываться на присвоение некоторым модификаторам определённых значений. В этом случае обработчик будет срабатывать только если модификатор получает подходящее значение. Ниже приведён пример.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}staticgetmodsEvents(){return{onMod: {'some-mod': {'some': function(modValue,modName){this.makeSomeNoise(modValue);},'someOther': function(modValue,modName){this.makeSomeNoise(modValue);}}}};}makeSomeNoise(noiseLevel=0){alert('Noise!'+(noiseLevel ? ' Going to noise level '+noiseLevel : ''));}}News.register()

Также можно навешивать обработчики на изменение модификаторов элементов блока. Работает это аналогично добавлению обаботчиков на изменение модиикаторов блока, только ключом будет не onMod, а onElemMod. Ниже пример.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}staticgetmodsEvents(){return{onElemMod: {'button': {'disabled': function(modValue,modName){this.makeSomeNoise(modValue);},}}};}makeSomeNoise(noiseLevel=0){alert('Noise!'+(noiseLevel ? ' Going to noise level '+noiseLevel : ''));}}News.register()

Автомодификаторы

Для блока можно включить автоматическое переключение определённых модификаторов при наступлении определённых событий блока. На текущий момент поддерживаются следующие события:

  • hover - наведение курсора мыши
  • press - клик
  • focus - получение фокуса

Для включения автомодификаторов нужно переопределить статический геттер mods() и вернуть из него строку, содержащую список необходимых модификаторов, разделённых пробелом.

importBEMfrom'tao-bem'classNewsextendsBEM.Block{staticgetblockName(){return'b-news';}staticgetmods(){return'hover press';}}News.register();

Взаимодействие между блоками

Как описывалось выше все блоки можно получить через регистр коллекций: BEM.Registry.getCollection('b-news'). Но тут есть небольшая тонкость.

Так как инициализация блоков происходит последовательно, то нет гарантиии, что в момент инициализации конкретного блока уже будут иметься в коллекции экземпляры других блоков. Поэтому внутри метода onInit не стоит надеяться найти BEM.Registкy коллекцию других блоков. Для решения данной проблемы существует 2 способа:

  1. Получить нужный блок через промис.
  2. Производить инициализацию только после создания экземпляров всех связанных блоков.

Оба способа работают только для единичных экземляров (появления первого инстанса), если требуется ожидать все экземляры блока, то придется придумывать что-то своё.

Получение блока через промис

Данный вариант подходит если нужно получить связанный блок однакратно для выполнения каких-то операций, никак не влияющих на поведение текущего блока.

Получить промис можно через регистр: BEM.Registry.waitBlock('block_name'), аргументом метода является строка с именем блока. Метод возвращает промис, который пока не поддерживает ошибок (будет ждать бесконечно), а в момент появления требуемого блока в регистре вызовет резолв промиса с параметром, являющимся объектом-экземляром данного блока. Пример:

BEM.Registry.waitBlock('b-gallery').then(gallery=>gallery.goto(0));

Ждем пока будет инициализирован блок b-gallery, после чего на нем вызываем метод goto

Ожидание связанных блоков

Тут все сложнее:

  • Для начала нужно указать классы всех связанных блоков через статический геттер relatedBlocks.
  • Внутри метода onInit произвести инициализацию только логики, не относящейся к связанным блокам. А остальную инициализацию проводить в методе onRelatedBlocksInit, который будет вызван только полсле того, как будут инициализированы все связанные блоки (или не будет вызван, если не будет нужных блоков - будьте осторожны с опечатками в именах).
  • После вызова onRelatedBlocksInit, как минимум, по одному экземляру для каждого из зависимых блоков будет находиться в регистре блоков. Но для упрощения доступа можно воспользоваться свойством relatedBlocks текущего блока, который содержит Set для первых найденных инстанцев для кадого из зависимых блоков.

Пример:

importBEMfrom'tao-bem'classGalleryextendsBEM.Block{staticgetblockName(){return'b-gallery';}staticgetrelatedBlocks(){return['b-gallery-controls','b-page'];}onRelatedBlocksInit(){this.relatedBlocks.get('b-page').initSlider();this.relatedBlocks.get('b-gallery-controls').setSlider(this);}}

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages