diff --git a/web_src/js/modules/dirauto.js b/web_src/js/modules/dirauto.js index c917bf8cf..cd90f8155 100644 --- a/web_src/js/modules/dirauto.js +++ b/web_src/js/modules/dirauto.js @@ -1,5 +1,6 @@ -// for performance considerations, it only uses performant syntax +import {isDocumentFragmentOrElementNode} from '../utils/dom.js'; +// for performance considerations, it only uses performant syntax function attachDirAuto(el) { if (el.type !== 'hidden' && el.type !== 'checkbox' && @@ -18,7 +19,7 @@ export function initDirAuto() { const len = mutation.addedNodes.length; for (let i = 0; i < len; i++) { const addedNode = mutation.addedNodes[i]; - if (addedNode.nodeType !== Node.ELEMENT_NODE && addedNode.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) continue; + if (!isDocumentFragmentOrElementNode(addedNode)) continue; if (addedNode.nodeName === 'INPUT' || addedNode.nodeName === 'TEXTAREA') attachDirAuto(addedNode); const children = addedNode.querySelectorAll('input, textarea'); const len = children.length; diff --git a/web_src/js/modules/tippy.js b/web_src/js/modules/tippy.js index 9b2f3691c..6eae0c916 100644 --- a/web_src/js/modules/tippy.js +++ b/web_src/js/modules/tippy.js @@ -1,4 +1,5 @@ import tippy, {followCursor} from 'tippy.js'; +import {isDocumentFragmentOrElementNode} from '../utils/dom.js'; const visibleInstances = new Set(); @@ -136,8 +137,6 @@ function attachChildrenLazyTooltip(target) { } } -const elementNodeTypes = new Set([Node.ELEMENT_NODE, Node.DOCUMENT_FRAGMENT_NODE]); - export function initGlobalTooltips() { // use MutationObserver to detect new "data-tooltip-content" elements added to the DOM, or attributes changed const observerConnect = (observer) => observer.observe(document, { @@ -152,11 +151,10 @@ export function initGlobalTooltips() { if (mutation.type === 'childList') { // mainly for Vue components and AJAX rendered elements for (const el of mutation.addedNodes) { - if (elementNodeTypes.has(el.nodeType)) { - attachChildrenLazyTooltip(el); - if (el.hasAttribute('data-tooltip-content')) { - attachLazyTooltip(el); - } + if (!isDocumentFragmentOrElementNode(el)) continue; + attachChildrenLazyTooltip(el); + if (el.hasAttribute('data-tooltip-content')) { + attachLazyTooltip(el); } } } else if (mutation.type === 'attributes') { diff --git a/web_src/js/utils/dom.js b/web_src/js/utils/dom.js index 64a6a5aff..4dc55a518 100644 --- a/web_src/js/utils/dom.js +++ b/web_src/js/utils/dom.js @@ -59,6 +59,17 @@ export function onDomReady(cb) { } } +// checks whether an element is owned by the current document, and whether it is a document fragment or element node +// if it is, it means it is a "normal" element managed by us, which can be modified safely. +export function isDocumentFragmentOrElementNode(el) { + try { + return el.ownerDocument === document && el.nodeType === Node.ELEMENT_NODE || el.nodeType === Node.DOCUMENT_FRAGMENT_NODE; + } catch { + // in case the el is not in the same origin, then the access to nodeType would fail + return false; + } +} + // autosize a textarea to fit content. Based on // https://github.com/github/textarea-autosize // ---------------------------------------------------------------------