html - Área de texto modal de arranque de iOS 11 Safari fuera del cursor
bootstrap-modal mobile-safari (15)
Con iOS 11 safari, el cursor del cuadro de texto de entrada está fuera del cuadro de texto de entrada. No entendimos por qué está teniendo este problema. Como puede ver, mi cuadro de texto enfocado es el ingreso de texto por correo electrónico, pero mi cursor está fuera de él. Esto solo sucede con iOS 11 Safari
¿Has probado viewport-fit = cover para la meta viewport?
Mira esto: https://ayogo.com/blog/ios11-viewport/
Añadir
position: fixed;
al
body
cuando modal está abierto.
$(document).ready(function($){
$("#myBtn").click(function(){
$("#myModal").modal("show");
});
$("#myModal").on(''show.bs.modal'', function () {
$(''body'').addClass(''body-fixed'');
});
$("#myModal").on(''hide.bs.modal'', function () {
$(''body'').removeClass(''body-fixed'');
});
});
.body-fixed {
position: fixed;
width: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<button type="button" class="btn btn-info btn-lg" id="myBtn">Open Modal</button>
<!-- Modal -->
<div class="modal fade" id="myModal" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Form</h4>
</div>
<div class="modal-body">
<div class="form-group">
<label class="control-label">Input #1</label>
<input type="text" class="form-control">
</div>
<div class="form-group">
<label class="control-label">Input #2</label>
<input type="text" class="form-control">
</div>
<div class="form-group">
<label class="control-label">Input #3</label>
<input type="text" class="form-control">
</div>
<div class="form-group">
<label class="control-label">Input #4</label>
<input type="text" class="form-control">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
Anular css modal y cambiar su
position
de
fixed
a
absolute
.modal {
position: absolute !important;
}
Como se mencionó anteriormente: establecer la
property
style.position
del
body
en
fixed
resuelve el problema de
iOS cursor misplacement
.
Sin embargo, esta ganancia tiene el costo de desplazarse por la fuerza a la parte superior de la página.
Afortunadamente, este nuevo problema de
UX
se puede negar sin mucha sobrecarga al aprovechar
HTMLElement.style
y
window.scrollTo()
.
La esencia básica es contrarrestar el
scroll to top
manipulando el estilo del elemento del
style.top
cuando se
mounting
.
Esto se hace usando el valor
YOffset
capturado por la variable
ygap
.
A partir de ahí, se trata simplemente de restablecer el estilo
style.top
a
0
y
style.top
a
style.top
la vista del usuario usando
window.scrollTo(0, ygap)
al
dismounting
.
Vea a continuación un ejemplo práctico.
// 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)
}
En caso de que alguien esté buscando una solución en vanilla js que funcione en IOS> 11.2 y no requiera ningún CSS adicional:
(function() {
if (!/(iPhone|iPad|iPod).*(OS 11_[0-2]_[0-5])/.test(navigator.userAgent)) return
document.addEventListener(''focusin'', function(e) {
if (!e.target.tagName == ''INPUT'' && !e.target.tagName != ''TEXTAREA'') return
var container = getFixedContainer(e.target)
if (!container) return
var org_styles = {};
[''position'', ''top'', ''height''].forEach(function(key) {
org_styles[key] = container.style[key]
})
toAbsolute(container)
e.target.addEventListener(''blur'', function(v) {
restoreStyles(container, org_styles)
v.target.removeEventListener(v.type, arguments.callee)
})
})
function toAbsolute(modal) {
var rect = modal.getBoundingClientRect()
modal.style.position = ''absolute''
modal.style.top = (document.body.scrollTop + rect.y) + ''px''
modal.style.height = (rect.height) + ''px''
}
function restoreStyles(modal, styles) {
for (var key in styles) {
modal.style[key] = styles[key]
}
}
function getFixedContainer(elem) {
for (; elem && elem !== document; elem = elem.parentNode) {
if (window.getComputedStyle(elem).getPropertyValue(''position'') === ''fixed'') return elem
}
return null
}
})()
Lo que esto hace es:
- Compruebe si el navegador es Safari en iOS 11.0.0 - 11.2.5
-
Escuche cualquier evento de
focusin
en la página -
Si el elemento enfocado es una
input
o un área detextarea
y está contenido en un elemento confixed
posiciónfixed
, cambie la posición del contenedor aabsolute
relación conscrollTop
y las dimensiones originales del contenedor. -
En el desenfoque, restaure la posición del contenedor a
fixed
.
Error frustrante, gracias por identificarlo. De lo contrario, estaría golpeando mi iphone o mi cabeza contra la pared.
La solución más simple es (1 línea de cambio de código):
Simplemente agregue el siguiente CSS al html o a un archivo css externo.
<style type="text/css">
.modal-open { position: fixed; }
</style>
Aquí hay un ejemplo de trabajo completo:
.modal-open { position: fixed; }
<link href="https://getbootstrap.com/docs/3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal" data-whatever="@mdo">Open modal for @mdo</button>
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal" data-whatever="@fat">Open modal for @fat</button>
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal" data-whatever="@getbootstrap">Open modal for @getbootstrap</button>
...more buttons...
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="exampleModalLabel">New message</h4>
</div>
<div class="modal-body">
<form>
<div class="form-group">
<label for="recipient-name" class="control-label">Recipient:</label>
<input type="text" class="form-control" id="recipient-name">
</div>
<div class="form-group">
<label for="message-text" class="control-label">Message:</label>
<textarea class="form-control" id="message-text"></textarea>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Send message</button>
</div>
</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://getbootstrap.com/docs/3.3/dist/js/bootstrap.min.js"></script>
Envié un problema aquí: https://github.com/twbs/bootstrap/issues/24059
Esas soluciones que usan
position: fixed
y corrección de posición basada en
scrollTop
funcionan realmente bien, pero algunas personas (incluyéndome a mí) tuvieron otro problema: el teclado / cursor no se muestra cuando las entradas están enfocadas.
Observé que el cursor / cursor solo funciona cuando
NO
utilizamos la
position: fixed
en el cuerpo.
Entonces, después de intentar varias cosas, dejé de usar este enfoque y decidí usar
position: relative
en el
body
y usar
scrollTop
para corregir
la posición superior del modal en su
lugar.
Ver código a continuación:
var iosScrollPosition = 0;
function isIOS() {
// use any implementation to return true if device is iOS
}
function initModalFixIOS() {
if (isIOS()) {
// Bootstrap''s fade animation does not work with this approach
// iOS users won''t benefit from animation but everything else should work
jQuery(''#myModal'').removeClass(''fade'');
}
}
function onShowModalFixIOS() {
if (isIOS()) {
iosScrollPosition = jQuery(window).scrollTop();
jQuery(''body'').css({
''position'': ''relative'', // body is now relative
''top'': 0
});
jQuery(''#myModal'').css({
''position'': ''absolute'', // modal is now absolute
''height'': ''100%'',
''top'': iosScrollPosition // modal position correction
});
jQuery(''html, body'').css(''overflow'', ''hidden''); // prevent page scroll
}
}
function onHideModalFixIOS() {
// Restore everything
if (isIOS()) {
jQuery(''body'').css({
''position'': '''',
''top'': ''''
});
jQuery(''html, body'').scrollTop(iosScrollPosition);
jQuery(''html, body'').css(''overflow'', '''');
}
}
jQuery(document).ready(function() {
initModalFixIOS();
jQuery(''#myModal'')
.on(''show.bs.modal'', onShowModalFixIOS)
.on(''hide.bs.modal'', onHideModalFixIOS);
});
Esta solución funcionó para mí y funciona bien en todos los navegadores en iOS.
.safari-nav-force{
/* Allows content to fill the viewport and go beyond the bottom */
height: 100%;
overflow-y: scroll;
/* To smooth any scrolling behavior */
-webkit-overflow-scrolling: touch;
}
JavsScript
var iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
$(''.modal'').on(''shown.bs.modal'', function () {
if (iOS && $(''.modal'').hasClass(''in'')){
$(''html,body'').addClass(''safari-nav-force'');
}
});
$(''.modal'').on(''hidden.bs.modal'', function () {
if (iOS && !$(''.modal'').hasClass(''in'')){
$(''html,body'').removeClass(''safari-nav-force'');
}
});
Este problema va más allá de Bootstrap y más allá de Safari. Es un error de pantalla completa en iOS 11 que ocurre en todos los navegadores. La solución anterior no soluciona este problema en todos los casos.
El error se informa en detalle aquí:
https://medium.com/@eirik.luka/how-to-fix-the-ios-11-input-element-in-fixed-modals-bug-aaf66c7ba3f8
Supuestamente ya lo reportaron a Apple como un error.
Este problema ya no es reproducible después de actualizar sus dispositivos Apple a iOS 11.3
La solución más fácil / más limpia:
body.modal-open { position: fixed; width: 100%; }
Personalmente,
position: fixed
desplazamiento
position: fixed
hacia arriba automáticamente
.
Bastante molesto !
Para evitar penalizar a otros dispositivos y versiones , aplico esta corrección solo a las versiones apropiadas de iOS.
** VERSIÓN 1 - Todas las correcciones modales **
Para el 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");
}
});
Para el CSS
/* Apply CSS to iOS affected versions only */
body.iosBugFixCaret.modal-open { position: fixed; width: 100%; }
** VERSIÓN 2 - Solo modales seleccionados **
Modifiqué la función para disparar solo para modales seleccionados con una clase
.inputModal
Solo los modales con entradas deben verse afectados para evitar el desplazamiento hacia arriba.
Para el 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);
}
});
}
})();
});
Para el CSS
/* Apply CSS to iOS affected versions only */
body.iosBugFixCaret.modal-open { position: fixed; width: 100%; }
Para el HTML Agregue la clase inputModal al modal
<div class="modal fade inputModal" tabindex="-1" role="dialog">
...
</div>
Nota bene La función javascript ahora se invoca automáticamente
** ACTUALIZACIÓN iOS 11.3 - Error corregido 😃🎉 **
A partir de iOS 11.3, el error ha sido corregido.
No hay necesidad de probar
OS 11_
iOS11 = /OS 11_0|OS 11_1|OS 11_2/.test(ua);
en
iOS11 = /OS 11_0|OS 11_1|OS 11_2/.test(ua);
Pero tenga cuidado ya que iOS 11.2 todavía se usa ampliamente (a partir de abril de 2018). Ver
Solucioné el problema agregando
position:fixed
al cuerpo al abrir un modal.
Espero que esto te ayudará.
agregar a la posición #modal: absoluto soluciona problemas futuros relacionados con la posición: arreglado
Para este tipo de problema encontramos las siguientes 3 soluciones
1) Cambiar el contenedor desde la
position: fixed;
position: absolute;
.
La desventaja de esto es que, en primer lugar, el usuario puede desplazarse fácilmente fuera del modo en que queríamos ocupar el espacio de la ventana.
2) Se podría argumentar que los modales no funcionan tan bien en pantallas más pequeñas y la mejor solución sería no usar modales en absoluto, pero esto sería una gran reescritura en nuestro caso, y necesitábamos solucionarlo lo antes posible. como sea posible.
3) Lo que encontramos que es la mejor solución en nuestro caso es mantener el modal fijo, pero estirarlo hasta los bordes de la ventana, ocultando el resto del contenido en el cuerpo con
display: none;
.
Esto eliminaría el problema de que la altura del cuerpo fuera más alta que la ventana gráfica y no habría desplazamiento de fondo al enfocar la entrada.
Descubre más detalles here
El enlace mencionado anteriormente obtendrá el estado del error, es decir, si se resuelve desde el lado del iPhone o no