Skip to content

fix(QTooltip): guard MutationObserver against detached innerRef#18316

Open
mathsgod wants to merge 1 commit into
quasarframework:devfrom
mathsgod:fix/qtooltip-mutation-observer-null-guard
Open

fix(QTooltip): guard MutationObserver against detached innerRef#18316
mathsgod wants to merge 1 commit into
quasarframework:devfrom
mathsgod:fix/qtooltip-mutation-observer-null-guard

Conversation

@mathsgod

Copy link
Copy Markdown

What & Why

QTooltip.handleShow registers a MutationObserver inside a nextTick callback. If the component unmounts before that microtask runs (e.g. fast route change while hovering an anchor that triggers a tooltip show), innerRef.value is null and the call throws:

Uncaught (in promise) TypeError: Failed to execute 'observe' on 'MutationObserver': parameter 1 is not of type 'Node'.

The thrown error is unhandled and surfaces in the console. It is most visible in apps that pair QTooltip with quick navigation (table action buttons, tabs, dialogs).

Reproduction

  1. Render a button with <q-tooltip>.
  2. Hover the button so the tooltip enters its show flow.
  3. Navigate to a new route before the next-tick callback runs (e.g. click a link in the same mouse-move tick).
  4. Console shows the MutationObserver TypeError.

Fix

Add a null check and wrap observer.observe(...) in a try/catch. If the ref is detached when the tick fires, silently abort positioning instead of throwing. The component is on its way out anyway.

       registerTick(() => {
         observer = new MutationObserver(() => updatePosition())
-        observer.observe(innerRef.value, {
-          attributes: false,
-          childList: true,
-          characterData: true,
-          subtree: true
-        })
+        if (innerRef.value === null) {
+          observer = void 0
+          return
+        }
+        try {
+          observer.observe(innerRef.value, {
+            attributes: false,
+            childList: true,
+            characterData: true,
+            subtree: true
+          })
+        } catch (_e) {
+          observer = void 0
+        }
         updatePosition()
         configureScrollTarget()
       })

Notes

  • The existing useTick composable already calls removeTick on onBeforeUnmount, so the pending tick is cancelled when possible. The null guard covers the remaining race where the tick fires after innerRef is detached but the VM is not yet considered destroyed (e.g. <keep-alive> deactivation, sibling unmount).
  • No public API change.

When the component unmounts before the next-tick callback in
`handleShow` runs (e.g. fast route change while hovering an anchor
that triggers tooltip show), `innerRef.value` is `null` and
`observer.observe(null)` throws:

  TypeError: Failed to execute 'observe' on 'MutationObserver':
  parameter 1 is not of type 'Node'.

Add a null check and wrap the observe call in a try/catch so that a
detached ref during navigation silently aborts positioning instead
of throwing an uncaught error.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant