fix: post-sweep follow-ups — prepend scroll under animate-leave, dev-server parity, ADR-002, docs-site loops callout - #326
Merged
Merged
Conversation
Mirror the test-server.js pattern: env override before sibling fallback, fs.stat graceful 404 when the Elements build is absent. Refs #317
Surface the loops "Common Mistakes" content (directive on container vs item) on the docs website. Adds the section with wrong/right code examples for lists and tables, plus a rule-of-thumb callout. All user-facing text uses t= i18n keys under docs.loops.commonMistakes with translations in all 5 locales (en, pt, es, fr, it). Refs #318
Drop "establishes the convention" — docs/adr/ already existed with ADR-001. Fix post-init guard line range: 528-534, not 530-535.
…te-leave When a loop has animate-leave, loops.js defers renderItems() behind animationend/setTimeout. The prepend scroll compensation read a zero scrollHeight delta and silently skipped adjustment, causing a visible scroll jump when the deferred render landed. Fall back to a MutationObserver on el that watches for the deferred render's childList changes and compensates once the scrollHeight delta becomes non-zero. The observer disconnects on height change or tree disposal (el.__disposers), with an el.isConnected guard. Refs #316
Replace curly/smart quotes with straight quotes in the ruleOfThumb i18n value across all 5 locale files (en, pt, es, fr, it) to match the codebase's established convention. Refs #318
docs(site): add Common Mistakes section to loops page
fix(http): prepend scroll compensation under animate-leave
"la unidad que se repete" → "se repite"
…branch The assignment at the end of the `if (isInsertMode && !_isFirstFetch)` block is unreachable-as-meaningful: the guard already requires _isFirstFetch to be false. The real transition from true to false happens in the replace-mode path.
Replace the MutationObserver fallback for deferred scroll compensation with an internal signal from loops.js. This eliminates four confirmed design defects in the original mechanism from PR #323: 1. Stale/accumulating observers: zero-delta sync renders no longer arm any mechanism — the signal is only set when loops.js actually defers renderItems() behind animate-leave. 2. Fetch-time scrollTop restored: compensation is now delta-based at fire time (scrollTop += delta) instead of restoring a stale absolute position captured before the animation window. 3. Unrelated mutations mis-trigger: no MutationObserver means unrelated childList changes (nested if/show, loading indicators) cannot fire premature or wrong adjustments. 4. get+each combo leak: el.isConnected guard skips compensation when the loop element is detached (self-repeating pattern), preventing observers on detached nodes and unreachable __disposers entries. Mechanism: loops.js sets parent._deferredRenderCallback = null when it defers renderItems behind animate-leave, and invokes/clears the callback after renderItems completes. http.js checks the signal after $set — if present, registers a delta-based compensation callback; if absent, compensates immediately from the measured delta. Tests 68-73 cover: zero-delta sync with no stale interference, deferred render compensation, user scroll preservation during deferral, unrelated DOM mutation immunity, disposal mid-defer cleanup, and get+each combo safety.
Addresses review feedback on PR #327: 1. BLOCKER — nesting: with <div get><ul><li each>, the signal landed on <ul> (loop parent) but http.js checked <div> (get-host). Fix: http.js pre-marks el with a _deferredRenderSlot object before $set; loops.js walks UP the ancestor chain from its parent to find the nearest ancestor with the slot. This bridges any nesting depth. Plain loops without an http.js ancestor pay only the walk cost (no slot found → no-op). 2. SHOULD — overlapping cycles: slot object identity serves as a generation guard. A new prepend cycle creates a new slot object; loops.js checks identity before invoking — if the slot was replaced by a newer cycle, the stale callback no-ops. 3. SHOULD — dead guard: removed the unreachable !el.isConnected branch (the _isLoopElement guard in http.js init bails before the prepend code is ever reached for get+each combo). 4. NIT — restored the rationale comment for pushing directly to el.__disposers instead of _onDispose() (async _currentEl drift). Tests 73-74 cover nested markup (el > ul > li[each]) with both sync and deferred (animate-leave) render paths. Test 72 updated for slot property name.
fix(http): rework prepend scroll compensation mechanism
This was referenced Aug 4, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Post-sweep follow-ups
Refs #316 #317 #318 #319
fix(http): prepend scroll compensation now survivesanimate-leavedeferred renders — MutationObserver fallback fires the adjustment when the deferred render lands (isConnected + disposal guards); comment corrected (synchronous only for plain loops). Tests 68-70. (PR fix(http): prepend scroll compensation under animate-leave #323)chore(dev-server):NOJS_ELEMENTS_PATHenv override + graceful 404, mirroring test-server.js. (PR chore: followup fixes for #317 and #319 #320)docs(adr): ADR-002 committed atdocs/adr/— the three shipped decisions (insert-mode single-source rendering, observer root, i18n post-init guard), every claim source-verified. (PR chore: followup fixes for #317 and #319 #320)docs(site): loops page now surfaces the "Common Mistakes" section —loops.tpl+ 5 i18n keys ×5 locales, key parity verified. (PR docs(site): add Common Mistakes section to loops page #321)Gate (merged branch)
Related but out of scope (tracked): #322, #324, #325, nojs-skill#74. Companion docs PR: no-js-dev/nojs-skill#73 (held).