With iOS 11 safari, input textbox cursor are outside of input textbox. We did not get why it is having this problem. As you can see my focused text box is email text input b
As previously mentioned: setting the style.position
property
of body
to fixed
solves the iOS cursor misplacement
issue.
However, this gain comes at the cost of being forcibly scrolled to the top of the page.
Fortunately, this new UX
problem can be negated without much overhead by leveraging HTMLElement.style and window.scrollTo().
The basic gist is to counteract the scroll to top
by manipulating the body
element's style.top
when mounting
. This is done using the YOffset
value captured by the ygap
variable.
From there it's simply a matter of resetting the body's
style.top
to 0
and reframing the user's view using window.scrollTo(0, ygap)
when dismounting
.
See below for a practical example.
// Global Variables (Manage Globally In Scope).
const body = document.querySelector('body') // Body.
let ygap = 0 // Y Offset.
// On Mount (Call When Mounting).
const onModalMount = () => {
// Y Gap.
ygap = window.pageYOffset || document.documentElement.scrollTop
// Fix Body.
body.style.position = 'fixed'
// Apply Y Offset To Body Top.
body.style.top = `${-ygap}px`
}
// On Dismount (Call When Dismounting).
const onModalDismount = () => {
// Unfix Body.
body.style.position = 'relative'
// Reset Top Offset.
body.style.top = '0'
// Reset Scroll.
window.scrollTo(0, ygap)
}
Override modal css and change its position
from fixed
to absolute
.modal {
position: absolute !important;
}
add to the #modal position:absolute it fix future issues related to the position: fixed
This issue goes beyond Bootstrap, and beyond just Safari. It is a full display bug in iOS 11 that occurs in all browsers. The fix above does not fix this issue in all instances.
The bug is reported in detail here:
https://medium.com/@eirik.luka/how-to-fix-the-ios-11-input-element-in-fixed-modals-bug-aaf66c7ba3f8
Supposedly they already reported it to Apple as a bug.
Have you try viewport-fit=cover for the meta viewport.
Look at this : https://ayogo.com/blog/ios11-viewport/
Personally, position: fixed
scroll to top automatically. Quite annoying !
To avoid penalizing other devices and versions I apply this fix only to the appropriate versions of iOS.
For the javascript/jQuery
$(document).ready(function() {
// Detect ios 11_x_x affected
// NEED TO BE UPDATED if new versions are affected
var ua = navigator.userAgent,
iOS = /iPad|iPhone|iPod/.test(ua),
iOS11 = /OS 11_0|OS 11_1|OS 11_2/.test(ua);
// ios 11 bug caret position
if ( iOS && iOS11 ) {
// Add CSS class to body
$("body").addClass("iosBugFixCaret");
}
});
For the CSS
/* Apply CSS to iOS affected versions only */
body.iosBugFixCaret.modal-open { position: fixed; width: 100%; }
I modified the function to fire only for selected modals with a class .inputModal
Only the modals with inputs should be impacted to avoid the scroll to top.
For the javascript/jQuery
$(document).ready(function() {
// Detect ios 11_x_x affected
// NEED TO BE UPDATED if new versions are affected
(function iOS_CaretBug() {
var ua = navigator.userAgent,
scrollTopPosition,
iOS = /iPad|iPhone|iPod/.test(ua),
iOS11 = /OS 11_0|OS 11_1|OS 11_2/.test(ua);
// ios 11 bug caret position
if ( iOS && iOS11 ) {
$(document.body).on('show.bs.modal', function(e) {
if ( $(e.target).hasClass('inputModal') ) {
// Get scroll position before moving top
scrollTopPosition = $(document).scrollTop();
// Add CSS to body "position: fixed"
$("body").addClass("iosBugFixCaret");
}
});
$(document.body).on('hide.bs.modal', function(e) {
if ( $(e.target).hasClass('inputModal') ) {
// Remove CSS to body "position: fixed"
$("body").removeClass("iosBugFixCaret");
//Go back to initial position in document
$(document).scrollTop(scrollTopPosition);
}
});
}
})();
});
For the CSS
/* Apply CSS to iOS affected versions only */
body.iosBugFixCaret.modal-open { position: fixed; width: 100%; }
For the HTML Add the class inputModal to the modal
<div class="modal fade inputModal" tabindex="-1" role="dialog">
...
</div>
Nota bene The javascript function is now self-invoking