Área de texto modal de arranque de Safari de iOS 11 fuera del cursor


85

Con iOS 11 safari, el cursor del cuadro de texto de entrada está fuera del cuadro de texto de entrada. No entendimos por qué tiene este problema. Como puede ver, mi cuadro de texto enfocado es la entrada de texto de correo electrónico, pero mi cursor está fuera de él. Esto solo sucede con iOS 11 Safari

Problema


1
Este es un error con Safari. Apple lo ha solucionado internamente, pero la solución aún no está en una versión pública (o incluso beta) de iOS. bugs.webkit.org/show_bug.cgi?id=176896
Beetle

Respuestas:


69

Solucioné el problema agregando position:fixedal cuerpo al abrir un modal. Espero que esto te ayudará.


6
Nota por las respuestas a continuación: Bootstrap agrega .modal-open al cuerpo cuando el modal está abierto, por lo que simplemente puede agregar la posición: fija a esa clase.
timmyc

1
Es posible que también deba agregar width:100%para restringir el ancho del cuerpo al ancho del dispositivo. En algunos casos, dependiendo del marcado existente, eso podría ser un problema. También me gusta la solución de @gentleboy (abajo) para no penalizar a otros navegadores sin el problema, porque al configurar el cuerpo como fijo hace que el cuerpo se desplace hacia arriba, lo cual es algo molesto.
Redtopia

Curiosamente, tuve este problema con una aplicación construida con meteor-cordova. Parecía que cualquier dispositivo iOS con v11 + tenía este problema. En mi caso ya lo había position:fixedsolicitado al cuerpo de la aplicación. En cambio, lo cambié a position:absoluteen el htmlelemento y solucionó mi problema. ¡Gracias Jen!
Adam Ross Bowers

1
Este podría ser el final de las formas modales para nosotros. Incluso si Apple soluciona el problema, dependerá del usuario actualizar su dispositivo para ver el formulario de trabajo. ¿No hay una forma universal más fácil de solucionar este problema? ¿Qué tal un relleno de polietileno?
Reado

1
me salvaste la vida <3
Cristian B.

42

Personalmente, position: fixed desplácese hacia arriba automáticamente . Bastante molesto !

Para evitar penalizar a otros dispositivos y versiones , aplico esta solución solo a las versiones apropiadas de iOS.


** VERSIÓN 1 - Se corrigen todos los modales **

Para 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 que se active solo para modales seleccionados con una clase .inputModal

Solo los modales con entradas deben verse afectados para evitar el desplazamiento hacia la parte superior.

Para 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


** ACTUALIZAR iOS 11.3 - Error corregido 😃🎉 **

A partir de iOS 11.3, se corrigió el error. No es necesario realizar la prueba OS 11_eniOS11 = /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

estadística 1

estadística 2


2
Buen consejo. No tengo este problema, pero supongo que podría ser una respuesta global. Lo
agregaré

3
@gentleboy ¿Por qué no utilizar REGEX para encontrar cualquier OS 11? ¿De esa manera no tendría que actualizar cada actualización de OS 11 a su código? algo como estoiOS11 = /OS 11_(\d{1,2})(_{0,1})(\d{1,2})/.test(us);
T. Evans

1
@RonakK parece que todavía está presente en la versión beta 11.2 y 11.3 según bugs.webkit.org
micaball

2
@ T.Evans que la expresión regular no funciona. Una expresión regular correcta podría ser algo como esto:ios11 = /OS 11_(\d{1,2})/.test(ua);
Abraham

2
La posición fija todavía llevará el desplazamiento del documento / cuerpo hacia arriba. Sugiero tomar la posición actual de scrollTop en modal abierto y readd scrollTop valor una vez cerrado modal. Como esto jsfiddle.net/im4aLL/hmvget9x/1
HADI

15

Este problema va más allá de Bootstrap y más allá de Safari. Es un error de visualización completo 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 informaron a Apple como un error.


Este parece ser el problema para mí, funcionaba bien antes de actualizar a IOS 11. Los usuarios de Android no tienen el problema.
hackingchemist

11

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


Esta debería ser la respuesta. Bootstrap agrega la clase modal-open al cuerpo cuando un modal es visible. Solo necesitas apuntar a esa clase.
lfkwtz

1
He estado lidiando con este problema durante algún tiempo. Esta solución no es adecuada para mí, ya que agregar posición fija desplaza la página hacia la parte superior. Entonces, cuando el usuario cierra el modal, se encuentra en una posición de desplazamiento diferente. Esto conduce a una experiencia de usuario terrible
Nearpoint

Esta solución funcionó para mí al usar Chrome en dispositivos móviles. Estaba confundido al principio ya que .modal-open no aparece en el html modal, pero lo agregué de todos modos como CSS en mi encabezado y funcionó.
David

4

La solución más fácil / limpia:

body.modal-open { position: fixed; width: 100%; }

1
Ésta es la solución más sencilla y funciona, pero el único problema es que el cursor desaparece por completo. No es tan malo como la situación inicial, pero hay un problema de usabilidad.
tmo256

Extraño, no he experimentado que un cursor desaparezca
lfkwtz

1
También experimenté la desaparición del cursor, ¿algún pensamiento sobre por qué podría suceder eso?
Alex Burgos

4
Sí, también veo un cursor que desaparece solo para el primer evento de enfoque. Los siguientes focos de entrada tienen el cursor apareciendo. Muy extraño. Esto solo sucede en Safari en iOS.
Devin Walker

@DevinWalker, ¿alguna vez resolvieron el problema del cursor que desaparece?
concesión

3

Este problema ya no se puede reproducir después de actualizar sus dispositivos Apple a iOS 11.3


¿Qué quieres decir con que Apple lo arregló?
Starscream

1
@Starscream, sí, Apple lo solucionó con esta versión de software (iOS 11.3)
Eashan

2

Agregar position: fixed;a bodycuando 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>


1
Al igual que en otras soluciones de este hilo, una vez que se cierra el modal, vuelve a estar en la parte superior. Por ejemplo: el usuario se desplaza a través de la cuadrícula de elementos, lanza detalles en un modal ... y con esta solución, cuando cierran el modal ... vuelven a estar en la parte superior de la cuadrícula de elementos y deben volver a desplazarse
bkwdesign

2
en mi iPhone 7 plus, esta solución hace que mi cursor desaparezca por completo
bkwdesign

1

Esas soluciones que usan una position: fixedcorrección de posición basada en scrollTopfuncionan muy bien, pero algunas personas (incluido yo) tienen otro problema: el cursor / cursor del teclado no se muestra cuando las entradas están enfocadas.

Observé que el cursor de intercalación funciona solo cuando NO lo usamos position: fixeden el cuerpo. Así que después de probar varias cosas, me di por vencido en el uso de este enfoque y se decantó por position: relativeel bodyuso scrollTopde la correcta posición de la parte superior de 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);
});

0

Como se mencionó anteriormente: configurar el style.position propertyde bodypara fixedresuelve el iOS cursor misplacementproblema.

Sin embargo, esta ganancia tiene el costo de ser desplazado a la fuerza a la parte superior de la página.

Afortunadamente, este nuevo UXproblema se puede solucionar sin demasiados gastos generales al aprovechar HTMLElement.styley window.scrollTo().

La esencia básica es contrarrestar el scroll to topmanipulando el cuándo bodydel elemento . Esto se hace utilizando el valor capturado por la variable.style.topmountingYOffsetygap

Desde allí es simplemente una cuestión de reajustar el body's style.topque 0y reformular la visión del usuario usando window.scrollTo(0, ygap)cuando 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)

}

¿Tiene un ejemplo más detallado para esto? Supongo que tendría el Mount en una función que se llama cuando se abre el modal, y luego pongo el Dismount en una función y lo llamaría cuando el modal está cerrado.
Neal Jones

Aahh sí. Veo a que te refieres. Consulte arriba para ver una solución revisada. También; sí, la intención es que los llames functionscuando mountingy dismounting. Gracias.
Arman Charan

-1

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 hace esto es:

  1. Compruebe si el navegador es Safari en iOS 11.0.0 - 11.2.5
  2. Escuche cualquier focusinevento en la página
  3. Si el elemento enfocado es un inputo un textareay está contenido en un elemento con fixedposición, cambie la posición del contenedor a absolutemientras mira scrollTopy las dimensiones originales del contenedor.
  4. En desenfoque, restaure la posición del contenedor a fixed.

-1

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');
    }
});


-2

Anular CSS modal y cambiar su positionde fixedaabsolute

.modal {
position: absolute !important;
}

1
No funcionó en mi escenario. Cambiar de fijo a absoluto puede tener MUCHOS efectos secundarios
Steve D

@SteveD: para que funcione, debe agregar su modal a <body>. Y el <cuerpo> debería tener - posición: relativa. Y cuando debería funcionar :)
Dan

-3

agregar a la posición #modal: absoluta, solucionar problemas futuros relacionados con la posición: arreglado


Tu respuesta es un duplicado exacto. Asegúrese de no publicar respuestas duplicadas.
MechMK1
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.