Phenomenon
A persistent regression in iOS 18 (Safari) affects scroll anchoring within contenteditable. Traditionally, when an element is focused, the browser is supposed to ensure the caret is visible. In recent versions, if the editor is inside a scrollable container (or the document itself is long), focusing the editor or running a command like bold causes the entire viewport to snap-jump to the top of the editor. This forces the user to manually scroll back down to find their cursor.
Reproduction Steps
- Create a webpage with enough content to cause vertical scrolling.
- Place a
contenteditabledivat the bottom of the page. - Scroll to the bottom and click inside the editor.
- Try to run
document.execCommand('italic')via a toolbar button. - Observe the viewport position after the command executes.
Observed Behavior
- The ‘Jump’: The scroll position of the
windowor the parentdivresets to the top edge of the editor container. - Scroll Inertia Break: If the user was scrolling while focus was triggered, the inertia is canceled, and the snap happens instantly.
Impact
- Disorienting UX: Users lose their context and must fight the browser to stay on the active line.
- Form Submission Issues: If the ‘Save’ button is at the bottom, and the viewport jumps, the user may accidentally click a header link instead.
Browser Comparison
- iOS Safari 18: High frequency of anchoring failure.
- Android Chrome: Generally keeps the caret in view, though it may trigger a layout “Resize” which affects fixed elements.
References & Solutions
Mitigation: Visual Viewport Manual Sync
Use the Visual Viewport API to detect focuses and manually trigger a scrollIntoView({ behavior: 'smooth', block: 'center' }) on a temporary span placed at the selection range.