Prerequisites
Ionic Framework Version
v9.x (@ionic/core 9.0.2, also reproduced on 9.0.1)
Current Behavior
On iOS, when a focused text field lives inside an ion-toolbar in a bottom
ion-footer, and an element is inserted ABOVE that field inside the same
toolbar, the text caret keeps its previous screen position instead of following
the field.
The field itself does not move (the footer is bottom-anchored, so it grows
upwards). The caret ends up drawn one inserted-element-height too high — visually
inside the newly added element, which is not editable at all.
The caret only corrects itself when the user types a real character. Programmatic
attempts to force a re-measure all have no effect: setSelectionRange() with the
same range, setSelectionRange() to a different range and back, setRangeText(''),
document.execCommand('insertText', ''), blur() + focus(), and toggling
inputMode off and back.
Expected Behavior
The caret should stay with the field it belongs to, as it does on every other
layout change.
Steps to Reproduce
- Open the reproduction on an iOS device or the iOS Simulator (Safari or a
WKWebView).
- Tap the text field in the footer so it has focus and shows a blinking caret.
- Wait 2.5s — a 60px block is revealed above the field, inside the same
ion-toolbar.
- The field stays put, but the caret is now drawn inside the gold block above it.
- Type one character: the caret jumps back to the correct place.
Code Reproduction URL
Self-contained single file, no build step, loads Ionic from the CDN:
https://gist.github.com/tzdevelopteam/2d444e8822c8dedbe43a4faa72df564a
(save the file and open it on an iOS device — it needs no build step)
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ionic/core@9.0.2/css/ionic.bundle.css">
<script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core@9.0.2/dist/ionic/ionic.esm.js"></script>
<ion-app>
<div class="ion-page">
<ion-content><p>tap the field, wait 2.5s</p></ion-content>
<ion-footer>
<ion-toolbar>
<div id="above" style="display:none;height:60px;background:gold">
the caret is wrongly drawn in here
</div>
<textarea id="field" rows="1" style="width:100%;font-size:16px">hello there</textarea>
</ion-toolbar>
</ion-footer>
</div>
</ion-app>
<script type="module">
const field = document.getElementById('field');
field.addEventListener('focus', () => {
setTimeout(() => (document.getElementById('above').style.display = 'block'), 2500);
}, { once: true });
</script>
Isolation already done
ion-toolbar is required. Putting the same markup directly in ion-footer
(no toolbar) makes the caret behave correctly. That is the smallest difference
between working and broken.
ion-content is NOT required — it reproduces with the footer alone.
- Not
ion-textarea: a plain light-DOM <textarea> reproduces it, as above.
- Not the iOS input shims:
initialize({ inputShims: false }) changes nothing.
- Not the toolbar's
contain: content: overriding it with contain: none changes
nothing.
- Not application CSS: reproduces with
ionic.bundle.css and nothing else.
- Hand-rolling the same geometry without Ionic components (an absolutely
positioned flex-column page with a growing flex-item footer) does NOT reproduce
it, so this appears specific to the toolbar's own structure.
Ionic Framework
Ionic Framework (@ionic/core)
Installed Packages
@ionic/core 9.0.2 (CDN), reproduced in an Angular app on @ionic/angular 9.0.1.
Additional Information
Reproduced on iOS 26.5 (iPhone 17 Simulator) in Safari, and on a physical iPhone
inside a Capacitor WKWebView. Not observed on Android/Chromium.
Impact is cosmetic — the caret is only mis-drawn, typed text still goes to the
right place, and the first keystroke fixes it. But in a chat composer (tap
"reply", a reply preview opens above the input) users see the caret sitting in
the preview for as long as they hesitate before typing.
Prerequisites
Ionic Framework Version
v9.x (@ionic/core 9.0.2, also reproduced on 9.0.1)
Current Behavior
On iOS, when a focused text field lives inside an
ion-toolbarin a bottomion-footer, and an element is inserted ABOVE that field inside the sametoolbar, the text caret keeps its previous screen position instead of following
the field.
The field itself does not move (the footer is bottom-anchored, so it grows
upwards). The caret ends up drawn one inserted-element-height too high — visually
inside the newly added element, which is not editable at all.
The caret only corrects itself when the user types a real character. Programmatic
attempts to force a re-measure all have no effect:
setSelectionRange()with thesame range,
setSelectionRange()to a different range and back,setRangeText(''),document.execCommand('insertText', ''),blur()+focus(), and togglinginputModeoff and back.Expected Behavior
The caret should stay with the field it belongs to, as it does on every other
layout change.
Steps to Reproduce
WKWebView).
ion-toolbar.Code Reproduction URL
Self-contained single file, no build step, loads Ionic from the CDN:
https://gist.github.com/tzdevelopteam/2d444e8822c8dedbe43a4faa72df564a
(save the file and open it on an iOS device — it needs no build step)
Isolation already done
ion-toolbaris required. Putting the same markup directly inion-footer(no toolbar) makes the caret behave correctly. That is the smallest difference
between working and broken.
ion-contentis NOT required — it reproduces with the footer alone.ion-textarea: a plain light-DOM<textarea>reproduces it, as above.initialize({ inputShims: false })changes nothing.contain: content: overriding it withcontain: nonechangesnothing.
ionic.bundle.cssand nothing else.positioned flex-column page with a growing flex-item footer) does NOT reproduce
it, so this appears specific to the toolbar's own structure.
Ionic Framework
Ionic Framework (@ionic/core)
Installed Packages
@ionic/core 9.0.2 (CDN), reproduced in an Angular app on @ionic/angular 9.0.1.
Additional Information
Reproduced on iOS 26.5 (iPhone 17 Simulator) in Safari, and on a physical iPhone
inside a Capacitor WKWebView. Not observed on Android/Chromium.
Impact is cosmetic — the caret is only mis-drawn, typed text still goes to the
right place, and the first keystroke fixes it. But in a chat composer (tap
"reply", a reply preview opens above the input) users see the caret sitting in
the preview for as long as they hesitate before typing.