Skip to content

Mobile: Fixes #15120: RTE heading links scroll to bottom instead of top in Editing mode#15128

Open
varunkumar-22 wants to merge 3 commits intolaurent22:devfrom
varunkumar-22:fix/mobile-rte-heading-scroll-15120
Open

Mobile: Fixes #15120: RTE heading links scroll to bottom instead of top in Editing mode#15128
varunkumar-22 wants to merge 3 commits intolaurent22:devfrom
varunkumar-22:fix/mobile-rte-heading-scroll-15120

Conversation

@varunkumar-22
Copy link
Copy Markdown
Contributor

Fixes #15120

Problem

When you tap a heading link in the rich text editor on mobile, the note scrolls but the heading ends up at the bottom of the screen. This is inconsistent with view mode, where the same action brings the heading to the top

Fix

The root cause was that ProseMirror's built-in scrollIntoView() uses a minimal-scroll strategy ; it only scrolls just enough to make the cursor visible, which naturally pushes the heading to the bottom. The fix grabs the actual heading DOM element using view.nodeDOM() and calls the native scrollIntoView({ block: 'start' }) on it directly, which is exactly what the view mode does

Test Plan

Tested Manually:

Screen.Recording.2026-04-16.at.10.mp4

@coderabbitai coderabbitai Bot added bug It's a bug mobile All mobile platforms editor labels Apr 16, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 16, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7b072848-e25b-4d13-9246-bf81cb477e54

📥 Commits

Reviewing files that changed from the base of the PR and between 1e9f826 and e3392d9.

📒 Files selected for processing (1)
  • packages/editor/ProseMirror/utils/jumpToHash.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/editor/ProseMirror/utils/jumpToHash.ts

📝 Walkthrough

Walkthrough

Added DOM-based scrolling after ProseMirror selection dispatch: the code now records the matched heading node position and, when a view is available, resolves the heading's DOM element and uses window scrolling to place the heading at the top of the viewport while keeping the existing selection/transaction behavior.

Changes

Cohort / File(s) Summary
Heading jump behaviour
packages/editor/ProseMirror/utils/jumpToHash.ts
Recorded a new targetHeadingNodePos alongside the existing selection anchor, preserved state.tr.setSelection(...).scrollIntoView() dispatch, and added a subsequent DOM-based scroll (resolve via view.nodeDOM(...), compute bounding rect, window.scrollTo(...)) when view is provided before focusing the editor. Lines changed: +10/-0.

Suggested reviewers

  • laurent22
  • mrjo118
  • personalizedrefrigerator
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: fixing mobile RTE heading link scrolling to position at top instead of bottom in editing mode.
Description check ✅ Passed The description clearly relates to the changeset, explaining the problem, root cause, and the fix applied to the heading scroll behaviour.
Linked Issues check ✅ Passed The code changes directly address issue #15120 by implementing native element scrolling to position headings at the top, matching view mode behaviour as required.
Out of Scope Changes check ✅ Passed The changes are focused on fixing the heading scroll position in the jumpToHash function and remain within the scope of addressing #15120.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@laurent22
Copy link
Copy Markdown
Owner

Please address this comment from Claude:

4. Removing .scrollIntoView() from the transaction

The original code used ProseMirror's tr.scrollIntoView() which also ensures the cursor is visible after selection changes (e.g., if the soft keyboard appears on mobile and reduces viewport). The PR removes this in favor of native scrollIntoView. If view.nodeDOM() returns null (e.g., for a virtual/off-screen node), neither scroll mechanism fires and the heading won't be scrolled into view at all. This is a potential silent failure — the original code at least always attempted to scroll.

@varunkumar-22
Copy link
Copy Markdown
Contributor Author

yeah! I have kept tr.scrollIntoView() on the transaction so it still acts as a fallback when nodeDOM returns null, and also handles the cursor visibility case when the soft keyboard appears
Also while looking into code again i realized , with scrollIntoView({ block: 'start' }) older android webviews don't support the options object form, so it would silently do nothing on those devices, i changed the approach like this:
when the DOM node is available, I used window.scrollTo with getBoundingClientRect to scroll the heading to the top of the viewport.window.scrollTo works reliably across all versions

@varunkumar-22
Copy link
Copy Markdown
Contributor Author

Still working well with the new approach:

Screen.Recording.2026-04-19.at.10.mp4

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

Labels

bug It's a bug editor mobile All mobile platforms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Mobile: Following heading links in the RTE scroll with the heading at the bottom, rather than at the top

3 participants