html ios bootstrap-modal mobile-safari ios11

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



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">&times;</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:

  1. Compruebe si el navegador es Safari en iOS 11.0.0 - 11.2.5
  2. Escuche cualquier evento de focusin en la página
  3. Si el elemento enfocado es una input o un área de textarea y está contenido en un elemento con fixed posición fixed , cambie la posición del contenedor a absolute relación con scrollTop y las dimensiones originales del contenedor.
  4. 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">&times;</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 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

stat 1

stat 2


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