diff --git a/index.js b/index.js index a845f5a..f0032de 100644 --- a/index.js +++ b/index.js @@ -10,6 +10,18 @@ const attributeToOption = (attribute) => { const isTouchDevice = () => 'ontouchstart' in window || navigator.maxTouchPoints > 0; +let visibilityListenerRegistered = false; +const handleVisibilityChange = () => { + if (document.hidden) { + // Скрываем все активные всплывающие подсказки + for (const $tooltip of document.querySelectorAll('.tooltip')) { + if ($tooltip._reference?._tooltip?.isVisible) { + $tooltip._reference._tooltip.hide({ immediately: true }); + } + } + } +}; + export const createTooltip = ($el, content, options) => { options = { animation: [ @@ -152,6 +164,10 @@ export const createTooltip = ($el, content, options) => { return; } + clearTimeout(showTimeout); + clearTimeout(hideTimeout); + cancelAnimationFrame(rafId); + $el._tooltip.$tooltip?.remove(); // Вызываем autoUpdateCleanup только если всплывающая подсказка была видна (иначе вызывать её не имеет смысла) @@ -282,43 +298,37 @@ export const createTooltip = ($el, content, options) => { showTimeout = setTimeout( async () => { - if (!$el._tooltip.isVisible) { - (options.appendTo === 'parent' ? $el.parentElement : options.appendTo).append( - $el._tooltip.$tooltip, - ); - $el._tooltip.isVisible = true; - $el._tooltip.autoUpdateCleanup = autoUpdate( - $el, - $el._tooltip.$tooltip, - $el._tooltip.updatePosition, - ); + // Проверяем $el._tooltip на сущестование + if (!$el._tooltip || $el._tooltip.isVisible) { + return; + } - if ( - options.hideOnClick && - (options.trigger.includes('click') || options.trigger.includes('manual')) - ) { - document.body.addEventListener('click', $el._tooltip.hideOnClickListener); - listeners.push({ - el: document.body, - event: 'click', - listener: $el._tooltip.hideOnClickListener, - }); - } + (options.appendTo === 'parent' ? $el.parentElement : options.appendTo).append($el._tooltip.$tooltip); + $el._tooltip.isVisible = true; + $el._tooltip.autoUpdateCleanup = autoUpdate($el, $el._tooltip.$tooltip, $el._tooltip.updatePosition); - if (options.onShow) { - options.onShow($el._tooltip); - } + if (options.hideOnClick && (options.trigger.includes('click') || options.trigger.includes('manual'))) { + document.body.addEventListener('click', $el._tooltip.hideOnClickListener); + listeners.push({ + el: document.body, + event: 'click', + listener: $el._tooltip.hideOnClickListener, + }); + } - try { - await $el._tooltip.$tooltip.querySelector('.tooltip__root').animate(options.animation[0], { - duration: immediately ? 0 : options.duration[0], - easing: options.easing[0], - }).finished; - } catch {} // eslint-disable-line no-empty + if (options.onShow) { + options.onShow($el._tooltip); + } - if (options.onShown) { - options.onShown($el._tooltip); - } + try { + await $el._tooltip.$tooltip.querySelector('.tooltip__root').animate(options.animation[0], { + duration: immediately ? 0 : options.duration[0], + easing: options.easing[0], + }).finished; + } catch {} // eslint-disable-line no-empty + + if (options.onShown) { + options.onShown($el._tooltip); } }, immediately ? 0 : options.delay[0], @@ -329,27 +339,35 @@ export const createTooltip = ($el, content, options) => { clearTimeout(showTimeout); hideTimeout = setTimeout( async () => { - if ($el._tooltip.isVisible) { - if (options.onHide) { - options.onHide($el._tooltip); - } + // Проверяем $el._tooltip на сущестование + if (!$el._tooltip || !$el._tooltip.isVisible) { + return; + } - try { - await $el._tooltip.$tooltip.querySelector('.tooltip__root').animate(options.animation[1], { - duration: immediately ? 0 : options.duration[1], - easing: options.easing[1], - }).finished; - } catch {} // eslint-disable-line no-empty + if (options.onHide) { + options.onHide($el._tooltip); + } - if ($el._tooltip.$tooltip) { - $el._tooltip.$tooltip.remove(); - } - $el._tooltip.isVisible = false; - $el._tooltip.autoUpdateCleanup(); + try { + await $el._tooltip.$tooltip.querySelector('.tooltip__root').animate(options.animation[1], { + duration: immediately ? 0 : options.duration[1], + easing: options.easing[1], + }).finished; + } catch {} // eslint-disable-line no-empty - if (options.onHidden) { - options.onHidden($el._tooltip); - } + // Ещё одна проверка на сущестование $el._tooltip после await + if (!$el._tooltip) { + return; + } + + if ($el._tooltip.$tooltip) { + $el._tooltip.$tooltip.remove(); + } + $el._tooltip.isVisible = false; + $el._tooltip.autoUpdateCleanup(); + + if (options.onHidden) { + options.onHidden($el._tooltip); } }, immediately ? 0 : options.delay[1], @@ -450,4 +468,9 @@ export const createTooltip = ($el, content, options) => { }; registerListeners(); + + if (!visibilityListenerRegistered) { + document.addEventListener('visibilitychange', handleVisibilityChange); + visibilityListenerRegistered = true; + } };