Контекстные меню

Tippy.js — это высоко настраиваемая библиотека для создания всплывающих подсказок и элементов интерфейса поверх HTML-элементов. Контекстные меню представляют собой особый тип всплывающих элементов, которые отображаются по клику правой кнопкой мыши или по определённым событиям пользователя. В Tippy.js такие меню строятся на основе инстансов Tippy, что позволяет гибко управлять их поведением и стилем.

import tippy from 'tippy.js';
import 'tippy.js/dist/tippy.css';

const contextMenu = tippy(document.querySelector('#menuTarget'), {
  content: '<ul><li>Пункт 1</li><li>Пункт 2</li><li>Пункт 3</li></ul>',
  allowHTML: true,
  interactive: true,
  trigger: 'manual',
  placement: 'right-start',
  animation: 'shift-away',
});

В этом примере создаётся всплывающее меню, которое не появляется автоматически, а управляется вручную через методы .show() и .hide(). Атрибут interactive: true позволяет взаимодействовать с содержимым меню, например, нажимать на пункты списка.

Настройка триггеров для контекстного меню

Контекстное меню традиционно открывается по событию contextmenu (правая кнопка мыши). Tippy.js позволяет переопределить стандартные триггеры:

const target = document.querySelector('#menuTarget');

target.addEventListener('contextmenu', (event) => {
  event.preventDefault();
  contextMenu[0].setProps({
    getReferenceClientRect: () => ({
      width: 0,
      height: 0,
      top: event.clientY,
      bottom: event.clientY,
      left: event.clientX,
      right: event.clientX,
    }),
  });
  contextMenu[0].show();
});

Использование getReferenceClientRect позволяет точно позиционировать меню в точке клика, а event.preventDefault() предотвращает стандартное системное меню браузера.

Интерактивность и управление событиями

Для контекстного меню критично, чтобы оно не исчезало при наведении курсора на элементы внутри. В Tippy.js это достигается через:

  • interactive: true — разрешает взаимодействие с содержимым.
  • hideOnClick: true | false | 'toggle' — контролирует поведение при кликах вне меню.
  • Обработчики событий onShow, onHide, onMount для управления динамическим содержимым.

Пример динамического меню с обработкой кликов:

const dynamicMenu = tippy(target, {
  content: '',
  allowHTML: true,
  interactive: true,
  trigger: 'manual',
  onShow(instance) {
    instance.setContent(`
      <ul>
        <li id="action1">Действие 1</li>
        <li id="action2">Действие 2</li>
      </ul>
    `);
    document.getElementById('action1').oncl ick = () => { console.log('Action 1'); instance.hide(); };
    document.getElementById('action2').oncl ick = () => { console.log('Action 2'); instance.hide(); };
  },
});

Позиционирование и адаптивность

Tippy.js использует Popper.js под капотом, что позволяет автоматически корректировать позицию меню при недостатке места на экране. Основные опции позиционирования:

  • placement — направление относительно целевого элемента (top, bottom, left, right, с модификаторами -start, -end).
  • offset — смещение по горизонтали и вертикали.
  • flip — автоматическое изменение позиции при переполнении видимой области.
  • boundary — определяет контейнер для проверки выхода за границы.
tippy(target, {
  content: 'Меню',
  placement: 'bottom-start',
  offset: [0, 10],
  flip: true,
  interactive: true,
});

Стайлизация и кастомизация

Tippy.js позволяет использовать собственные CSS-классы для оформления контекстных меню. Атрибут theme или className задаёт стиль, который можно определить в CSS:

tippy(target, {
  content: '<ul><li>Пункт A</li><li>Пункт B</li></ul>',
  allowHTML: true,
  interactive: true,
  theme: 'custom-menu',
});

CSS пример:

.tippy-box[data-theme~='custom-menu'] {
  background-color: #2c3e50;
  color: #ecf0f1;
  border-radius: 6px;
  padding: 8px;
}

.tippy-box[data-theme~='custom-menu'] ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

.tippy-box[data-theme~='custom-menu'] li {
  padding: 6px 12px;
  cursor: pointer;
}

.tippy-box[data-theme~='custom-menu'] li:hover {
  background-color: #34495e;
}

Управление множественными меню

При работе с несколькими элементами важно создавать отдельные инстансы для каждого таргета или использовать делегирование:

tippy.delegate(document.body, {
  target: '.has-menu',
  content: 'Динамическое меню',
  allowHTML: true,
  interactive: true,
  trigger: 'manual',
});

Метод delegate позволяет привязать одно поведение ко многим элементам без необходимости создавать отдельный инстанс для каждого.

Дополнительные возможности

  • Анимации: Tippy.js поддерживает встроенные анимации (fade, scale, shift-away) и позволяет подключать кастомные.
  • Lazy loading: меню можно формировать только при открытии, экономя ресурсы.
  • События жизненного цикла: onCreate, onMount, onShow, onHide, onDestroy дают полный контроль над поведением меню.
  • Адаптивные размеры: через CSS и Popper.js можно динамически менять ширину или высоту меню под содержимое.

Tippy.js сочетает гибкость позиционирования, богатую настройку интерактивности и возможности визуальной кастомизации, делая создание контекстных меню простым и эффективным.