Skip to content

fix: Improve hash scrolling restoration logic for better user experience - #2952

Merged
renejeglinsky merged 1 commit into
mainfrom
rjegl/fix-hash-issue
Oct 20, 2026
Merged

renejeglinsky merged 1 commit into
mainfrom
rjegl/fix-hash-issue

Conversation

@renejeglinsky

@renejeglinsky renejeglinsky commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Here's what I found and fixed.

Why the anchor disappears

The culprit is the inline head script restoreCodeGroupPreferences.js, which is injected into every page via config.js:93.

Its job is to restore code-group tab preferences before Vue hydrates. Because re-selecting tabs changes layout height, it can't let the browser scroll to the hash natively, so on load it strips the hash from the URL (87), remembers it, and plans to put it back and scroll manually once the tabs are applied.

The bug: every path that restores the hash was gated on code-groups being present:

  • Restore-if-already-present runs only when initialCodeGroupCount > 0.
  • The MutationObserver restores only when a .vp-code-group node is added.
  • The DOMContentLoaded restore is registered only when readyState === 'loading', but this inline script actually runs at readyState interactive/complete, so that branch never registers.

I confirmed this with a hash timeline: the hash is present until ~500ms, then gets stripped and never comes back on /docs/java/ai. A page with code-groups (/docs/cds/aspects#separation-of-concerns) kept its hash, which matched the theory exactly: java/ai has zero .vp-code-group elements, so nothing ever triggers the restore.

The fix

Two changes in restoreCodeGroupPreferences.js:

  1. restoreHashScroll now puts the hash back immediately and only marks itself done once the target element actually exists, so late-hydrating content (dev mode especially) still gets scrolled.
  2. Added a pollRestore fallback that runs via requestAnimationFrame whenever a hash is pending, independent of code-groups. It retries each frame until the target hydrates (3s cap), then stops. This covers pages with no code-groups and any readyState.

--- AI-supported ---

@hyperspace-pr-bot

Copy link
Copy Markdown
Contributor

Control Panel

Hi, I'm an AI-powered Review Bot that helps you with summarizing and reviewing pull requests.
To interact with me, just use the following actions:

  • 📝 Summarize PR
  • 🔍 Review
  • 🗑️ Delete all bot comments and reviews

@chgeo chgeo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would have taken me ages to figure this out. Thanks!

@renejeglinsky
renejeglinsky added this pull request to the merge queue Oct 20, 2026
Merged via the queue into main with commit 52b4107 Oct 20, 2026
4 checks passed
@renejeglinsky
renejeglinsky deleted the rjegl/fix-hash-issue branch October 20, 2026 07:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants