Pathological DOM patterns for PixieBrix's jQueryInitialize
selector watcher — the shared MutationObserver behind both trigger
and button starter bricks — reworked in the 3.2.6 milestone.
Why this exists. 3.2.6 shipped a series of performance overhauls to the watcher. A customer reported the changes made some pages crash. These pages reproduce the cases the work targeted, so each can be observed — and the old vs new watcher behavior compared — on the same DOM.
Two ways to use each page:
1. With the extension — install a real trigger/button mod against the
listed selector and load the page.
2. Without it — use the built-in harness panel (top-right). It runs the
real, vendored jQueryInitialize (bundled from
pixiebrix-source; see source/trigger-performance/vendor)
and reports FPS, long-tasks/sec, max task time, and callback matches. Switch
the watcher mode to compare the actual code paths:
off baseline · legacy real pre-3.2.6 ·
optimized real 3.2.6.
| Scenario | Pathological pattern | Target selector | PR / tier |
|---|---|---|---|
| mutation-storm | High-rate attribute thrash + node add/remove; background-tab scanning | .storm-item |
#8170 T1 throttle, visibility |
| route-swap | SPA route change replacing 500+ siblings at once; ancestor-gated masking targets | body.route-active .pii |
#8170 burst #8202 T1S re-sweep |
| infinite-scroll-burst | Large burst appends on a timer; unbounded growth → memory pressure | .feed-row |
#8170 T1 rIC burst flush |
| many-selectors | Hundreds–thousands of watched selectors / observers at once (O(N²) legacy) | .sel-0 … .sel-N |
#8207 T2 token index |
| attribute-cascade | Insert then mutate value/aria-*/data-* + id/class |
[data-testid="row"], .loaded .field |
#8207 T2 attributeFilter |
| detached-subtree | Add-then-remove subtree in one task (React portal / failed diff) | .portal-content |
#8170 T1 isConnected skip |
| deep-dom | Very large / deeply nested DOM; expensive full-document scans | .wrapper .deep-target |
scan cost (optional) |
Pages that are smooth on their own and run fine with the right watcher, but land in a blind spot of the 3.2.6 optimizations — so optimized stays slow (or is worse than legacy). Each isolates one recent PR's limit. Useful as regression signals, not just legacy-vs-new contrasts.
| Scenario | Pathological pattern | Target selector | Defeats |
|---|---|---|---|
| has-sweep | Large static grid + light class toggles; :has() selector forces a
full-document Sizzle scan every throttle window |
.card:has(.badge.active) |
#8207 token index (complex fallback) |
| history-thrash | Static DOM, zero mutations; unique URL per frame (scroll/filter sync) → unthrottled full-document sweep per frame. Worse than legacy | .route-active .pii |
#8202 T1S route re-sweep |
| attr-escalation | Legitimate per-frame style churn; one co-installed
[data-state] mod escalates the shared observer to unfiltered
attributes:true for the whole page |
.benign-item + [data-state="open"] |
#8207 T2 attributeFilter |
The same patterns driven by real React /
react-router-dom, so the watcher runs against genuine React
commits — the way these cases occur in customer apps.
| Scenario | What React does | Target selector | PR / tier |
|---|---|---|---|
| react / render-storm | Rapid setState → reconciliation churn (remount or in-place patch) |
.storm-item |
#8170 T1 |
| react / route-swap | react-router navigation mounts/unmounts a large route subtree in one commit | .route-active .pii |
#8170 burst #8202 re-sweep |
| react / infinite-scroll | Growing list reconciled on each burst; unbounded with keep=0 |
.feed-row |
#8170 rIC flush |
| react / portal-churn | createPortal subtree mounted/unmounted rapidly (modal/tooltip) |
.portal-content |
#8170 add/remove |
The "optimized still degrades" cases above, driven through
real React commits (memoized static trees, ref-based animation, setState
reconciliation) — the way these patterns occur in customer apps.
| Scenario | What React does | Target selector | Defeats |
|---|---|---|---|
| react / has-sweep | setState toggles .active on a large grid; :has()
selector forces a full-document Sizzle scan per window |
.card:has(.badge.active) |
#8207 token index (complex fallback) |
| react / history-thrash | Memoized static tree; URL synced per frame → unthrottled full-document sweep. Worse than legacy | .route-active .pii |
#8202 T1S route re-sweep |
| react / attr-escalation | Ref-based per-frame style animation; one [data-state] mod
escalates the shared observer to attributes:true |
.benign-item + [data-state="open"] |
#8207 T2 attributeFilter |
Every knob lives in the URL — a crashing
configuration is a shareable link, e.g.
mutation-storm.html?harness=legacy&rate=2000&nodes=20000&autostart=1.