Repository navigation
⚠️ BREAKING CHANGES
Detection now runs once per mutation batch, not once per record (#208)
The MutationObserver callback used to run the detector once for each MutationRecord in a batch, awaiting each run before starting the next. It now runs the detector once per callback, against the current DOM.
For the default isExist detector and for plain selector or customMatcher checks, the result is the same: every extra pass in the old loop re-queried the same final DOM state and got the same answer. The difference was cost. A batch of hundreds of records (for example, a script that sets ~30 inline styles one by one on every dragover) ran the same query hundreds of times and could block the main thread for 0.5–2s.
What may break: the old loop gave the detector one extra try per record, and because each try was awaited, the tries were spread over time. A detector whose answer depends on something a MutationObserver doesn't report could become true between two of those tries and be picked up by chance. Examples:
- state that changes without a DOM mutation, such as
input.value,checked,HTMLImageElement.complete, layout and computed style, scroll position, or application state outside the DOM - an async detector that waits for something (a timer, a fetch, an animation frame) and only succeeds after a while
In 4.x, such a detector could still resolve inside a busy batch. In 5.0.0, it is asked once per batch. If it returns isDetected: false, it isn't asked again until the next mutation inside target that observeConfigs covers. If no such mutation comes, the promise stays pending until you abort it.
Migration: if your detector depends on non-DOM state, make sure a matching mutation follows the change you're waiting for, widen observeConfigs so it covers that mutation (for example characterData), or poll that state yourself instead of relying on waitElement. Selector-only usage, isExist, and isNotExist need no change.
Thanks @prichey for the report and fix.
🐛 Bug Fixes
Mutations during the initial check are no longer missed (#209)
waitElement ran its initial check and called observer.observe() only after it finished. Mutations made while that check was awaiting produced no records, so the element could be missed and the promise never settled. Two cases that used to hang:
// A microtask queued before the call appends the element
Promise.resolve().then(() => document.body.append(el));
await waitElement("#el");
// The element is appended while an async detector runs the initial check
waitElement("#el", { detector: slowAsyncDetector });
setTimeout(() => document.body.append(el), 10);The observer now starts before the initial check. Aborting during the initial check also no longer leaves the observer attached.
Note that the initial check and an observer callback can now overlap, so a custom detector may be called again before its previous call has finished. The promise still settles only once, with whichever call detects first. If your detector has side effects, make sure they tolerate overlapping calls.
🧰 Maintenance
- Switch the package manager to pnpm 12 (#207, #212) and publish to the npm registry explicitly through
publishConfig.registry(#213) - Update dev dependencies and CI actions: TypeScript 6.0.3, Vitest 4.1.11, jsdom 29.1.1, Biome 2.5.11, Node.js 24.20.0, pnpm/action-setup v6, and others
All commits
- chore: publish to the npmjs registry explicitly (#213) f76fae9
- chore: drop the hash from the packageManager field (#212) 0dfa646
- chore(deps): update pnpm/action-setup action to v6 (#152) 9979ef1
- chore(deps): update pnpm to v12 (#207) 8a949f2
- fix: start observing before the initial check (#209) 7284de0
- fix: check once per mutation batch instead of once per record (#208) 827c457
- chore(deps): update dependency @biomejs/biome to v2.5.11 (#206) 6763888
- chore(deps): update node.js to v24.20.0 (#205) b6df8ce
- chore(deps): update dependency @biomejs/biome to v2.5.10 (#204) 23a729a
- chore(deps): update vitest monorepo to v4.1.11 (#203) 62da4be
- chore(deps): update dependency @biomejs/biome to v2.5.9 (#202) cd942db
- chore(deps): update dependency @biomejs/biome to v2.5.8 (#201) ddf8621
- chore(deps): update node.js to v24.19.0 (#200) ffdaad9
- chore(deps): update dependency @biomejs/biome to v2.5.7 (#199) 953a74e
- chore(deps): update node.js to v24.18.1 (#197) c1ae043
- chore(deps): update dependency @biomejs/biome to v2.5.6 (#196) e22ded2
- chore(deps): update dependency @biomejs/biome to v2.5.5 (#195) ed2c368
- chore(deps): update dependency @biomejs/biome to v2.5.4 (#194) 70b2f13
- chore(deps): update pnpm to v10.34.5 (#192) 321be94
- chore(deps): update dependency @biomejs/biome to v2.5.3 (#190) bcb0152
- chore(deps): update vitest monorepo to v4.1.10 (#189) 377730f
- chore(deps): update dependency @biomejs/biome to v2.5.2 (#188) 349225b
- chore(deps): update node.js to v24.18.0 (#187) 12aea71
- chore(deps): update pnpm to v10.34.4 (#186) b1842bb
- chore(deps): update dependency @biomejs/biome to v2.5.1 (#185) e6a1c6c
- chore(deps): update dependency @biomejs/biome to v2.5.0 (#182) ac728f7
- chore(deps): update node.js to v24.17.0 (#183) ebeac21
- chore(deps): update vitest monorepo to v4.1.9 (#181) 31e7933
- chore(deps): update pnpm to v10.34.3 (#180) 5c4cf43
- chore(deps): update pnpm to v10.34.2 (#179) 0bc2296
- chore(deps): update vitest monorepo to v4.1.8 (#178) db978fd
- chore(deps): update pnpm to v10.34.1 (#176) 43ee919
- chore(deps): update pnpm to v10.34.0 (#175) 2b697cd
- chore(deps): update dependency @biomejs/biome to v2.4.16 (#174) b760553
- chore(deps): update vitest monorepo to v4.1.7 (#173) 0c35215
- chore(deps): update node.js to v24.16.0 (#172) 8c651b9
- chore(deps): update vitest monorepo to v4.1.6 (#171) 88fec1b
- chore(deps): update dependency @types/jsdom to v28.0.2 (#170) 29f1c99
- chore(deps): update dependency @biomejs/biome to v2.4.15 (#169) c396908
- chore(deps): update pnpm to v10.33.4 (#168) ee5ad2b
- chore(deps): update pnpm to v10.33.3 (#166) ae31677
- chore(deps): update dependency @biomejs/biome to v2.4.14 (#165) bbfb8c5
- chore(deps): update dependency jsdom to v29.1.1 (#164) b662815
- chore(deps): update dependency jsdom to v29.1.0 (#163) c38e0c6
- chore(deps): update dependency @biomejs/biome to v2.4.13 (#162) 696450d
- chore(deps): update pnpm to v10.33.2 (#161) 8ae8e96
- chore(deps): update pnpm to v10.33.1 (#160) 82327d3
- chore(deps): update vitest monorepo to v4.1.5 (#159) 29c5134
- chore(deps): update dependency typescript to v6.0.3 (#158) 9660ff5
- chore(deps): update dependency @biomejs/biome to v2.4.12 (#156) be497f8
- chore(deps): update node.js to v24.15.0 (#155) cf11173
- chore(deps): update dependency @biomejs/biome to v2.4.11 (#154) e8d9a4f
- chore(deps): update vitest monorepo to v4.1.4 (#153) fbcbbde
- chore(deps): update vitest monorepo to v4.1.3 (#151) 2e32b8a
- chore(deps): update dependency jsdom to v29.0.2 (#150) 546e389
- chore(deps): update dependency defu to v6.1.7 (#149) f5bdb50
Full Changelog: v4.2.0...v5.0.0