В архитектуре Popper.js модификаторы (modifiers) представляют собой ключевой механизм расширения поведения позиционирования. Условные модификаторы — это модификаторы, которые применяются только при выполнении определённых условий. Они позволяют динамически управлять логикой позиционирования без изменения основного кода и без создания отдельных экземпляров Popper.
Условность достигается за счёт:
enabled)Каждый модификатор в Popper.js описывается объектом со следующими свойствами:
{
name: 'exampleModifier',
enabled: true,
phase: 'main',
fn({ state, options, name, instance }) {
// логика модификатора
},
options: {},
requires: []
}
Условность чаще всего реализуется через:
enabledfnenabledСамый простой способ создать условный модификатор — управлять его
активностью через флаг enabled.
createPopper(reference, popper, {
modifiers: [
{
name: 'flip',
enabled: window.innerWidth > 768
}
]
});
В данном примере модификатор flip будет работать только
на экранах шире 768px.
Особенности:
enabled вычисляется при инициализацииPopper.js позволяет изменять модификаторы после создания экземпляра
через метод setOptions.
instance.setOptions(options => ({
...options,
modifiers: options.modifiers.map(mod =>
mod.name === 'offset'
? { ...mod, enabled: shouldEnableOffset() }
: mod
)
}));
Такой подход позволяет:
fnБолее гибкий подход — проверка условий непосредственно в функции модификатора:
{
name: 'conditionalOffset',
enabled: true,
phase: 'main',
fn({ state }) {
if (state.rects.reference.width > 200) {
state.modifiersData.popperOffsets.x += 10;
}
}
}
Преимущества:
state)state для условийОбъект state содержит всю информацию о текущем
положении:
state.rects — размеры reference и popperstate.placement — текущее размещениеstate.modifiersData — данные других модификаторовПример:
fn({ state }) {
if (state.placement.startsWith('top')) {
state.styles.popper.backgroundColor = 'red';
}
}
Таким образом можно создавать поведение, зависящее от текущего положения элемента.
requires)Модификаторы могут зависеть от других модификаторов. Это позволяет строить цепочки условий.
{
name: 'customModifier',
requires: ['offset'],
fn({ state }) {
if (state.modifiersData.offset) {
// логика при наличии offset
}
}
}
Если зависимый модификатор отключён, условие автоматически не выполняется.
Часто требуется учитывать несколько факторов одновременно:
fn({ state }) {
const isMobile = window.innerWidth < 768;
const isBottom = state.placement === 'bottom';
if (isMobile && isBottom) {
state.modifiersData.popperOffsets.y += 20;
}
}
Такой подход позволяет:
optionsМодификаторы могут менять своё поведение через options,
которые также можно формировать динамически:
{
name: 'offset',
options: {
offset: ({ placement }) => {
if (placement === 'top') {
return [0, 20];
}
return [0, 10];
}
}
}
Функция внутри options получает доступ к состоянию и
может возвращать разные значения.
Каждый модификатор выполняется в определённой фазе
(phase):
beforeReadreadafterReadbeforeMainmainafterMainbeforeWritewriteafterWriteИногда требуется выполнять логику только при определённых условиях, связанных с фазой:
fn({ state }) {
if (state.elements.reference instanceof HTMLElement) {
// выполнять только для DOM-элементов
}
}
createPopper(reference, tooltip, {
modifiers: [
{
name: 'offset',
options: {
offset: ({ placement }) => {
return placement === 'top' ? [0, 16] : [0, 8];
}
}
},
{
name: 'adaptiveBehavior',
enabled: true,
phase: 'main',
fn({ state }) {
if (window.innerWidth < 600) {
state.placement = 'bottom';
}
}
}
]
});
Здесь:
Условные модификаторы могут опираться на пользовательские параметры:
const isDebugMode = true;
{
name: 'debugModifier',
enabled: isDebugMode,
fn({ state }) {
console.log(state);
}
}
Это удобно для:
Чрезмерное использование условий внутри fn может
повлиять на производительность, особенно при частых обновлениях (scroll,
resize).
Рекомендуется:
enabledfn1. Изменение enabled без обновления
Popper
modifier.enabled = false; // не сработает
Требуется:
instance.setOptions(...)
2. Игнорирование фаз выполнения
Некоторые данные доступны только на определённых этапах. Проверки должны учитывать фазу.
3. Конфликт модификаторов
Несколько условных модификаторов могут изменять одни и те же данные:
state.modifiersData.popperOffsets
Это может приводить к непредсказуемым результатам.
1. Feature Toggle
enabled: features.enableNewPositioning
2. Context-aware модификаторы
if (state.elements.reference.dataset.type === 'menu')
3. Responsive модификаторы
if (window.matchMedia('(max-width: 768px)').matches)
enabledfnsetOptionsstateУсловные модификаторы превращают Popper.js из простого инструмента позиционирования в гибкую систему, способную адаптироваться к любым сценариям интерфейса.