Удаление и очистка экземпляров

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

Каждый вызов tippy() возвращает экземпляр (или массив экземпляров), который предоставляет методы для управления состоянием подсказки, включая удаление.

Основной метод удаления:

instance.destroy();

Этот метод полностью уничтожает экземпляр подсказки, включая:

  • удаление DOM-элемента подсказки
  • очистку всех обработчиков событий
  • удаление внутренних ссылок и данных

После вызова destroy() экземпляр становится невалидным и не может быть использован повторно.


Поведение destroy()

Метод destroy() выполняет несколько этапов:

  1. Удаление tooltip-элемента из DOM

    • Если подсказка была смонтирована (показана хотя бы один раз), её элемент удаляется
  2. Отключение событий

    • Все слушатели (hover, focus, click и т.д.) удаляются
  3. Очистка ссылок

    • Внутренние свойства экземпляра обнуляются
    • Ссылка ._tippy у целевого элемента удаляется
  4. Сброс состояния

    • Экземпляр больше не реагирует на методы (show, hide и т.д.)

Удаление через ссылку на DOM-элемент

Каждый DOM-элемент, к которому привязан Tippy, получает свойство _tippy, содержащее экземпляр:

const button = document.querySelector('#btn');
button._tippy.destroy();

Это удобно, если прямой доступ к переменной экземпляра отсутствует.


Массовое удаление экземпляров

Если tippy() был вызван с селектором, возвращается массив экземпляров:

const instances = tippy('.item');

instances.forEach(instance => {
  instance.destroy();
});

Это стандартный способ очистки группы подсказок.


Очистка при динамическом обновлении интерфейса

В приложениях с частыми изменениями DOM (например, SPA) необходимо вручную уничтожать экземпляры перед удалением элементов:

function removeElement(el) {
  if (el._tippy) {
    el._tippy.destroy();
  }
  el.remove();
}

Игнорирование этого приводит к:

  • утечкам памяти
  • «висящим» обработчикам событий
  • потенциальным ошибкам

Разница между hide() и destroy()

Важно различать:

Метод Описание
hide() Скрывает подсказку, но сохраняет экземпляр
destroy() Полностью удаляет экземпляр

Пример:

instance.hide();    // подсказка скрыта, но может быть снова показана
instance.destroy(); // экземпляр уничтожен навсегда

Повторная инициализация после удаления

После вызова destroy() можно создать новый экземпляр:

instance.destroy();

const newInstance = tippy(element, {
  content: 'Новая подсказка'
});

Это полезно при изменении конфигурации, которую нельзя обновить через setProps().


Очистка делегированных экземпляров

При использовании делегирования (tippy.delegate) создаётся родительский экземпляр:

const delegateInstance = tippy.delegate('.container', {
  target: '.child',
  content: 'Подсказка'
});

Удаление:

delegateInstance.destroy();

Это удаляет:

  • сам делегирующий экземпляр
  • все связанные дочерние подсказки

Автоматическое уничтожение при удалении узла

Tippy не отслеживает автоматически удаление DOM-элементов, поэтому:

element.remove(); // НЕ уничтожает tippy

Правильный подход:

if (element._tippy) {
  element._tippy.destroy();
}
element.remove();

Очистка при использовании фреймворков

React

В useEffect:

useEffect(() => {
  const instance = tippy(ref.current, { content: '...' });

  return () => {
    instance.destroy();
  };
}, []);

Vue

В хуке beforeUnmount:

beforeUnmount() {
  if (this.$el._tippy) {
    this.$el._tippy.destroy();
  }
}

Проверка существования экземпляра

Перед удалением важно убедиться, что экземпляр существует:

if (element._tippy) {
  element._tippy.destroy();
}

Иначе возможна ошибка доступа к undefined.


Освобождение памяти

Правильное использование destroy() напрямую влияет на:

  • производительность
  • потребление памяти
  • стабильность интерфейса

Особенно важно в:

  • длинных списках
  • виртуализированных таблицах
  • интерфейсах с частыми обновлениями

Частичные альтернативы destroy()

В некоторых случаях вместо полного удаления используются:

disable()

instance.disable();
  • отключает реакцию на события
  • экземпляр остаётся в памяти

enable()

instance.enable();
  • возвращает работоспособность

Это полезно для временного отключения без повторной инициализации.


Принудительное удаление tooltip DOM

Если требуется удалить только DOM-элемент подсказки:

instance.popper.remove();

Но это не рекомендуется, так как:

  • внутреннее состояние остаётся
  • возможны ошибки при следующем show()

Корректный способ — всегда использовать destroy().


Типичные ошибки

1. Удаление элемента без destroy():

element.remove(); // ошибка

2. Повторный вызов destroy():

instance.destroy();
instance.destroy(); // может вызвать ошибку

3. Потеря ссылки на экземпляр:

tippy('.btn'); // без сохранения
// позже нет доступа к destroy()

Рекомендации по управлению жизненным циклом

  • Всегда сохранять ссылку на экземпляр или использовать element._tippy
  • Уничтожать экземпляры перед удалением DOM-узлов
  • Использовать destroy() вместо ручного удаления элементов
  • В SPA — очищать экземпляры при размонтировании компонентов
  • Для временного отключения использовать disable(), а не destroy()

Взаимодействие с setProps и destroy()

Если требуется изменить настройки:

instance.setProps({
  content: 'Новое содержимое'
});

Но если меняется фундаментальное поведение (например, стратегия позиционирования), предпочтительнее:

instance.destroy();
tippy(element, newOptions);

Внутренние флаги состояния

После уничтожения:

  • instance.state.isDestroyed === true
  • вызовы методов игнорируются или приводят к ошибкам

Проверка:

if (!instance.state.isDestroyed) {
  instance.destroy();
}

Вывод практического подхода

  • destroy() — основной инструмент очистки
  • отсутствие очистки ведёт к утечкам
  • управление жизненным циклом особенно важно в динамических интерфейсах
  • правильная интеграция с фреймворками предотвращает накопление «мертвых» экземпляров