diff --git a/packages/csr/src/reactive-runtime.ts b/packages/csr/src/reactive-runtime.ts index 0dcdd0e6..ebb99d25 100644 --- a/packages/csr/src/reactive-runtime.ts +++ b/packages/csr/src/reactive-runtime.ts @@ -2886,6 +2886,32 @@ export const REACTIVE_RUNTIME = String.raw` }); } + /* + * One document observer, several subscribers. The filter stays explicit: + * observing every attribute would see the tabindex the roving code writes + * and loop on its own output. + */ + var documentWatchers = []; + + function watchDocument(handler) { + documentWatchers.push(handler); + } + + function startDocumentWatch() { + if (window.__wrnexusDocWatchBound || typeof MutationObserver !== "function") return; + window.__wrnexusDocWatchBound = true; + new MutationObserver(function () { + for (var index = 0; index < documentWatchers.length; index += 1) documentWatchers[index](); + }).observe(document.documentElement, { + subtree: true, + childList: true, + attributes: true, + // prettier-ignore + attributeFilter: ["data-open","data-show","class","style","hidden", + "data-placement","aria-selected","aria-current","disabled","aria-disabled"], + }); + } + function setupAnchoredOverlays() { if (window.__wrnexusAnchoredBound) return; window.__wrnexusAnchoredBound = true; @@ -2894,14 +2920,7 @@ export const REACTIVE_RUNTIME = String.raw` // root, data-show on the panel, a class), so watch for any attribute or // structural change and re-measure on the next frame instead of trying to // enumerate every signal. - if (typeof MutationObserver === "function") { - new MutationObserver(scheduleAnchoredReposition).observe(document.documentElement, { - subtree: true, - childList: true, - attributes: true, - attributeFilter: ["data-open", "data-show", "class", "data-placement"], - }); - } + watchDocument(scheduleAnchoredReposition); window.addEventListener("resize", scheduleAnchoredReposition); window.addEventListener("scroll", scheduleAnchoredReposition, true); @@ -3027,19 +3046,11 @@ export const REACTIVE_RUNTIME = String.raw` if (window.__wrnexusDialogsBound) return; window.__wrnexusDialogsBound = true; - if (typeof MutationObserver === "function") { - new MutationObserver(function () { - // Same deferral as the anchored clamp: the mutation that opens a - // dialog is the one that makes it visible, so it still measures as - // hidden until the panel has been laid out. - window.setTimeout(syncDialogs, 0); - }).observe(document.documentElement, { - subtree: true, - childList: true, - attributes: true, - attributeFilter: ["data-open", "data-show", "class", "style", "hidden"], - }); - } + // Deferred: the mutation that opens a dialog is the one that makes it + // visible, so it is not laid out yet when the record arrives. + watchDocument(function () { + window.setTimeout(syncDialogs, 0); + }); document.addEventListener("keydown", trapDialogTab, true); syncDialogs(); @@ -3157,16 +3168,9 @@ export const REACTIVE_RUNTIME = String.raw` document.addEventListener("keydown", handleRovingKeydown, true); - if (typeof MutationObserver === "function") { - new MutationObserver(function () { - window.setTimeout(syncRovingGroups, 0); - }).observe(document.documentElement, { - subtree: true, - childList: true, - attributes: true, - attributeFilter: ["aria-selected", "aria-current", "disabled", "aria-disabled", "hidden"], - }); - } + watchDocument(function () { + window.setTimeout(syncRovingGroups, 0); + }); syncRovingGroups(); } @@ -3240,11 +3244,9 @@ export const REACTIVE_RUNTIME = String.raw` for (var index = 0; index < navs.length; index += 1) wireScrollspy(navs[index]); }; wireAll(); - if (typeof MutationObserver === "function") { - new MutationObserver(function () { - window.setTimeout(wireAll, 0); - }).observe(document.documentElement, { subtree: true, childList: true }); - } + watchDocument(function () { + window.setTimeout(wireAll, 0); + }); } function hydrateScopes(root) { @@ -5466,6 +5468,7 @@ export const REACTIVE_RUNTIME = String.raw` setupModalDialogs(); setupRovingFocus(); setupScrollspy(); + startDocumentWatch(); window.__wrnexusRepositionAnchored = repositionAnchored; window.__wrnexusHydrateScopes = hydrateScopes; window.__wrnexusInvalidateClientModule = function (url) { clientModuleCache.delete(url); };