Skip to content

Conversation

@ilonatommy
Copy link
Member

@ilonatommy ilonatommy commented Oct 15, 2025

Prevent visual flash during navigation

This is a timing bug, specially visible on browsers with slower connection or when the page intentionally delays rendering. Because enhanced page load fetches and applies the new DOM asynchronously, the immediate scroll-up run against the previous page's DOM. User sees a page A jump to the top and only after the fetch of new page is completed - the page B replaces it.

Description

  • Enhanced navigation now defers scroll resets until the updated DOM is applied. When we leave the current path, NavigationEnhancement schedules ScrollResetSchedule.AfterDocumentUpdate, removing the flash that occurred while the old DOM was still visible.
  • Boot.Web invokes resetScrollIfNeeded(ScrollResetSchedule.AfterDocumentUpdate) from the shared documentUpdated callback so every enhanced navigation (including streaming SSR) scrolls exactly once, right after new content arrives.
  • Renderer now tracks pending resets with the ScrollResetSchedule enum and only calls window.scrollTo when the requested phase triggers, instead of scrolling immediately.
  • Interactive routing reuses the same scheduler: NavigationManager requests AfterBatch when client-side navigation changes pages, letting the render batch complete before the scroll reset.
  • Added an enhanced-navigation scroll regression test that watches the JS scroll override log alongside DOM updates, proving the page only resets after new markup renders so the original flicker can’t regress.
  • Updated other E2E suites to consume the shared WebDriverExtensions.IsStale helpers and the scroll observation utilities so we rely on a single stale-element check and avoid copy/pasted extension method logic.

Fixes #64015

@ilonatommy ilonatommy self-assigned this Oct 15, 2025
@github-actions github-actions bot added the area-blazor Includes: Blazor, Razor Components label Oct 15, 2025
@ilonatommy ilonatommy added this to the 10.0.x milestone Oct 15, 2025
@ilonatommy ilonatommy marked this pull request as ready for review October 23, 2025 13:03
@ilonatommy ilonatommy requested a review from a team as a code owner October 23, 2025 13:03
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes a visual timing bug in enhanced navigation where scroll resets occurred before new content rendered, causing a noticeable flash. The solution introduces a new ScrollResetSchedule enum to coordinate scroll timing with DOM updates.

Key changes:

  • Enhanced navigation now defers scroll resets until after new DOM content is applied
  • Added ScrollResetSchedule enum to track different timing phases for scroll resets
  • Implemented scroll position tracking in E2E tests to verify the fix

Reviewed Changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
FormWithParentBindingContextTest.cs Uses shared IsStale() extension method instead of local implementation
EnhancedNavigationTest.cs Adds scroll timing regression tests with new observation utilities
WebDriverExtensions.cs Introduces IsStale() extension and scroll observation infrastructure
ScrollOverrideScope.cs New test utility that intercepts and logs window.scrollTo calls
NavigationManager.ts Updates to use new scheduleScrollReset API with AfterBatch timing
NavigationEnhancement.ts Schedules scroll resets for AfterDocumentUpdate instead of immediate execution
Renderer.ts Implements ScrollResetSchedule enum and timing-based scroll reset logic
Boot.Web.ts Triggers AfterDocumentUpdate scroll resets in the shared documentUpdated callback

shouldResetScrollAfterNextBatch = true;
}
export function scheduleScrollReset(timing: ScrollResetSchedule): void {
if (timing != ScrollResetSchedule.AfterBatch) {
Copy link

Copilot AI Oct 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use strict inequality operator !== instead of != for type-safe comparison.

Suggested change
if (timing != ScrollResetSchedule.AfterBatch) {
if (timing !== ScrollResetSchedule.AfterBatch) {

Copilot uses AI. Check for mistakes.

Copy link
Member

@maraf maraf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me 👍

Copy link
Member

@oroztocil oroztocil left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great. Only added a request for documentation.

Comment on lines +15 to +19
export enum ScrollResetSchedule {
None,
AfterBatch,
AfterDocumentUpdate,
}
Copy link
Member

@oroztocil oroztocil Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to add comments explaining the values? When someone comes to this a year later they might have harder time understanding the difference between AfterBatch and AfterDocumentUpdate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-blazor Includes: Blazor, Razor Components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enhanced navigation in .NET10: scroll happens before content renders, causing visual flash

3 participants