diff options
Diffstat (limited to 'web_src/js/modules/tippy.js')
-rw-r--r-- | web_src/js/modules/tippy.js | 75 |
1 files changed, 49 insertions, 26 deletions
diff --git a/web_src/js/modules/tippy.js b/web_src/js/modules/tippy.js index 0d57af4f0f..6cec95d766 100644 --- a/web_src/js/modules/tippy.js +++ b/web_src/js/modules/tippy.js @@ -6,7 +6,7 @@ export function createTippy(target, opts = {}) { animation: false, allowHTML: false, hideOnClick: false, - interactiveBorder: 30, + interactiveBorder: 20, ignoreAttributes: true, maxWidth: 500, // increase over default 350px arrow: `<svg width="16" height="7"><path d="m0 7 8-7 8 7Z" class="tippy-svg-arrow-outer"/><path d="m0 8 8-7 8 7Z" class="tippy-svg-arrow-inner"/></svg>`, @@ -36,6 +36,8 @@ export function createTippy(target, opts = {}) { * @returns {null|tippy} */ function attachTooltip(target, content = null) { + switchTitleToTooltip(target); + content = content ?? target.getAttribute('data-tooltip-content'); if (!content) return null; @@ -55,6 +57,18 @@ function attachTooltip(target, content = null) { return target._tippy; } +function switchTitleToTooltip(target) { + const title = target.getAttribute('title'); + if (title) { + target.setAttribute('data-tooltip-content', title); + target.setAttribute('aria-label', title); + // keep the attribute, in case there are some other "[title]" selectors + // and to prevent infinite loop with <relative-time> which will re-add + // title if it is absent + target.setAttribute('title', ''); + } +} + /** * Creating tooltip tippy instance is expensive, so we only create it when the user hovers over the element * According to https://www.w3.org/TR/DOM-Level-3-Events/#events-mouseevent-event-order , mouseover event is fired before mouseenter event @@ -67,48 +81,57 @@ function lazyTooltipOnMouseHover(e) { attachTooltip(this); } -/** - * Activate the tooltip for all children elements - * And if the element has no aria-label, use the tooltip content as aria-label - * @param target {HTMLElement} - */ -function attachChildrenLazyTooltip(target) { - for (const el of target.querySelectorAll('[data-tooltip-content]')) { - el.addEventListener('mouseover', lazyTooltipOnMouseHover, true); +// Activate the tooltip for current element. +// If the element has no aria-label, use the tooltip content as aria-label. +function attachLazyTooltip(el) { + el.addEventListener('mouseover', lazyTooltipOnMouseHover, {capture: true}); - // meanwhile, if the element has no aria-label, use the tooltip content as aria-label - if (!el.hasAttribute('aria-label')) { - const content = target.getAttribute('data-tooltip-content'); - if (content) { - el.setAttribute('aria-label', content); - } + // meanwhile, if the element has no aria-label, use the tooltip content as aria-label + if (!el.hasAttribute('aria-label')) { + const content = el.getAttribute('data-tooltip-content'); + if (content) { + el.setAttribute('aria-label', content); } } } +// Activate the tooltip for all children elements. +function attachChildrenLazyTooltip(target) { + for (const el of target.querySelectorAll('[data-tooltip-content]')) { + attachLazyTooltip(el); + } +} + +const elementNodeTypes = new Set([Node.ELEMENT_NODE, Node.DOCUMENT_FRAGMENT_NODE]); + export function initGlobalTooltips() { - // use MutationObserver to detect new elements added to the DOM, or attributes changed - const observer = new MutationObserver((mutationList) => { - for (const mutation of mutationList) { + // use MutationObserver to detect new "data-tooltip-content" elements added to the DOM, or attributes changed + const observerConnect = (observer) => observer.observe(document, { + subtree: true, + childList: true, + attributeFilter: ['data-tooltip-content', 'title'] + }); + const observer = new MutationObserver((mutationList, observer) => { + const pending = observer.takeRecords(); + observer.disconnect(); + for (const mutation of [...mutationList, ...pending]) { if (mutation.type === 'childList') { // mainly for Vue components and AJAX rendered elements for (const el of mutation.addedNodes) { - // handle all "tooltip" elements in added nodes which have 'querySelectorAll' method, skip non-related nodes (eg: "#text") - if ('querySelectorAll' in el) { + if (elementNodeTypes.has(el.nodeType)) { attachChildrenLazyTooltip(el); + if (el.hasAttribute('data-tooltip-content')) { + attachLazyTooltip(el); + } } } } else if (mutation.type === 'attributes') { - // sync the tooltip content if the attributes change attachTooltip(mutation.target); } } + observerConnect(observer); }); - observer.observe(document, { - subtree: true, - childList: true, - attributeFilter: ['data-tooltip-content'], - }); + observerConnect(observer); attachChildrenLazyTooltip(document.documentElement); } |