iOS 11 Safari bootstrap area di testo modale al di fuori del cursore


85

Con iOS 11 Safari, il cursore della casella di testo di input si trova al di fuori della casella di testo di input. Non abbiamo capito perché ha questo problema. Come puoi vedere la mia casella di testo focalizzata è un input di testo e-mail ma il mio cursore si trova al di fuori di essa. Questo accade solo con iOS 11 Safari

Problema


1
Questo è un bug con Safari. Apple lo ha risolto internamente ma la correzione non è ancora in una versione pubblica (o addirittura beta) di iOS. bugs.webkit.org/show_bug.cgi?id=176896
Beetle

Risposte:


69

Ho risolto il problema aggiungendo position:fixedal corpo all'apertura di un modale. Spero che questo ti possa aiutare.


6
Nota per le risposte seguenti: Bootstrap aggiunge .modal-open al corpo quando il modal è aperto, quindi puoi semplicemente aggiungere position: fixed a quella classe.
timmyc

1
Potrebbe anche essere necessario aggiungere width:100%per vincolare la larghezza del corpo alla larghezza del dispositivo. In alcuni casi, a seconda del markup esistente, potrebbe essere un problema. Mi piace anche la soluzione di @gentleboy (sotto) per non penalizzare altri browser senza il problema, perché quando si imposta il body su fixed fa scorrere il body verso l'alto, il che è un po 'fastidioso.
Redtopia

Stranamente, ho avuto questo problema con un'applicazione compilata con meteor-cordova. Sembrava che qualsiasi dispositivo iOS con v11 + avesse questo problema. Nel mio caso avevo già position:fixedapplicato al corpo della domanda. Invece l'ho cambiato position:absolutein htmlsull'elemento e ha risolto il mio problema. Grazie Jen!
Adam Ross Bowers

1
Questa potrebbe essere la fine delle forme modali per noi. Anche se Apple risolve il problema, spetterà all'utente aggiornare il proprio dispositivo per vedere il modulo funzionante. Non esiste un modo universale più semplice per risolvere questo problema? Che ne dici di un riempimento in polietilene?
Reado

1
mi hai salvato la vita <3
Cristian B.

42

Personalmente, position: fixed scorri automaticamente verso l'alto . Piuttosto fastidioso!

Per evitare di penalizzare altri dispositivi e versioni, applico questa correzione solo alle versioni appropriate di iOS.


** VERSIONE 1 - Tutti i modali risolti **

Per 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");

    }

});

Per il CSS

/* Apply CSS to iOS affected versions only */
body.iosBugFixCaret.modal-open { position: fixed; width: 100%; }

** VERSIONE 2 - Solo modali selezionati **

Ho modificato la funzione per sparare solo per modali selezionati con una classe .inputModal

Solo i modali con input dovrebbero essere influenzati per evitare lo scorrimento verso l'alto.

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

        }
    })();
});

Per il CSS

/* Apply CSS to iOS affected versions only */
body.iosBugFixCaret.modal-open { position: fixed; width: 100%; }

Per l'HTML aggiungere la classe inputModal al modal

<div class="modal fade inputModal" tabindex="-1" role="dialog">
    ...
</div>

Nota bene La funzione javascript ora si richiama automaticamente


** AGGIORNA iOS 11.3 - Bug corretto 😃🎉 **

A partire da iOS 11.3, il bug è stato corretto. Non è necessario eseguire il test OS 11_iniOS11 = /OS 11_0|OS 11_1|OS 11_2/.test(ua);

Ma fai attenzione perché iOS 11.2 è ancora ampiamente utilizzato (ad aprile 2018). Vedere

stat 1

stat 2


2
Bel consiglio. Non ho questo problema ma immagino che potrebbe essere globale come risposta. Lo aggiungerò dopo aver finito le mie birre
micaball

3
@gentleboy Perché non utilizzare un REGEX per trovare qualsiasi OS 11? In questo modo non dovresti aggiornare ogni aggiornamento OS 11 al tuo codice? qualcosa del genereiOS11 = /OS 11_(\d{1,2})(_{0,1})(\d{1,2})/.test(us);
T. Evans

1
@RonakK sembra che sia ancora presente nelle beta 11.2 e 11.3 secondo bugs.webkit.org
micaball

2
@ T.Evans quella regex non funziona. Una regex corretta potrebbe essere qualcosa del genere:ios11 = /OS 11_(\d{1,2})/.test(ua);
Abraham

2
La posizione fissa continuerà a portare il documento / il corpo in alto. Suggerisco di afferrare la posizione scrollTop corrente su modal open e leggere il valore scrollTop una volta chiuso modale. Come questo jsfiddle.net/im4aLL/hmvget9x/1
HADI

15

Questo problema va oltre Bootstrap e oltre il semplice Safari. È un bug di visualizzazione completo in iOS 11 che si verifica in tutti i browser. La correzione sopra non risolve questo problema in tutti i casi.

Il bug è riportato in dettaglio qui:

https://medium.com/@eirik.luka/how-to-fix-the-ios-11-input-element-in-fixed-modals-bug-aaf66c7ba3f8

Presumibilmente l'hanno già segnalato ad Apple come un bug.


Questo sembra essere il problema per me, funzionava bene prima dell'aggiornamento a IOS 11. Gli utenti Android non hanno il problema.
hackingchemist

11

Bug frustrante, grazie per averlo identificato. Altrimenti sbatterei il mio iPhone o la mia testa contro il muro.

La soluzione più semplice è (1 riga di modifica del codice):

Basta aggiungere il seguente CSS all'html o a un file CSS esterno.

<style type="text/css">
.modal-open { position: fixed; }
</style>

Ecco un esempio funzionante 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>

Ho inviato un problema qui: https://github.com/twbs/bootstrap/issues/24059


Questa dovrebbe essere la risposta. Bootstrap aggiunge la classe modal-open al corpo quando è visibile un modal. Devi solo scegliere come target quella classe.
lfkwtz

1
Mi occupo di questo problema da tempo. Questa soluzione per me è inadeguata in quanto l'aggiunta di posizione fissa fa scorrere la pagina verso l'alto. Quindi, quando l'utente chiude il modale, si trova in una posizione di scorrimento diversa. Ciò porta a un'esperienza utente terribile
Nearpoint

Questa correzione ha funzionato per me nell'utilizzo di Chrome su dispositivi mobili. All'inizio ero confuso poiché .modal-open non appare nell'html modale, ma l'ho aggiunto comunque come CSS nella mia intestazione e ha funzionato.
David,

4

Soluzione più semplice / pulita:

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

1
Questa è la soluzione più semplice e funziona, ma l'unico problema è che il cursore scompare del tutto. Non è così grave come la situazione iniziale, ma c'è un problema di usabilità.
tmo256

Strano, non ho riscontrato la scomparsa del cursore
lfkwtz

1
Ho anche sperimentato la scomparsa del cursore, qualche idea sul perché potrebbe accadere?
Alex Burgos

4
Sì, vedo anche un cursore che scompare solo per il primo evento focus. I successivi focus di input hanno il cursore che appare. Molto strano. Questo accade solo in Safari su iOS.
Devin Walker

@DevinWalker avete mai risolto il problema del cursore che scompare?
concedere il

3

Questo problema non è più riproducibile dopo aver aggiornato i tuoi dispositivi Apple a iOS 11.3


Cosa vuoi dire, Apple l'ha risolto?
Starscream

1
@Starscream sì, è stato risolto da Apple con questa versione del software (iOS 11.3)
Eashan

2

Aggiungi position: fixed;a bodyquando modale è aperto.

$(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
Simile ad altre soluzioni su questo thread, una volta che il modale si chiude, sei di nuovo in cima. Ad esempio: l'utente scorre la griglia di elementi, lancia i dettagli in un modale .. e con questa correzione, quando chiude il modale .. sono di nuovo in cima alla griglia di elementi e devono scorrere nuovamente
bkwdesign

2
sul mio iPhone 7 plus, questa correzione fa scomparire del tutto il mio cursore
bkwdesign

1

Quelle soluzioni che utilizzano position: fixede la correzione della posizione in base a scrollTopfunzionano molto bene, ma alcune persone (me compreso) hanno riscontrato un altro problema: il cursore / il cursore della tastiera non vengono visualizzati quando gli input sono focalizzati.

Ho notato che l'accento circonflesso / cursore funziona solo quando NON lo usiamo position: fixedsul corpo. Così, dopo aver provato diverse cose, ho rinunciato a utilizzare questo approccio e ha deciso di utilizzare position: relativesu bodye l'uso scrollTopdi corretta posizione di vertice di modali , invece.

Vedere il codice di seguito:

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

Come accennato in precedenza: l'impostazione style.position propertydi di bodysu fixedrisolve il iOS cursor misplacementproblema.

Tuttavia, questo guadagno ha il costo di uno scorrimento forzato fino all'inizio della pagina.

Fortunatamente, questo nuovo UXproblema può essere risolto senza troppe spese generali sfruttando HTMLElement.stylee window.scrollTo().

L'essenza di base è contrastare la scroll to topmanipolazione bodydell'elemento style.topquando mounting. Questo viene fatto utilizzando il YOffsetvalore catturato dalla ygapvariabile.

Da lì è semplicemente una questione di reimpostare la body's style.topa 0e riformulare la vista dell'utente usando window.scrollTo(0, ygap)quando dismounting.

Vedi sotto per un esempio pratico.

// 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)

}

Hai un esempio più dettagliato per questo? Immagino che avrei il Mount in una funzione che viene chiamata quando si apre il modal, quindi inserisco Dismount in una funzione e lo chiamerei quando il modal è chiuso.
Neal Jones

Aahh sì. Capisco quello che vuoi dire. Vedere sopra per una soluzione rivista. Anche; sì, l'intento è che tu chiami quelli functionsquando mountinge dismounting. Grazie.
Arman Charan

-1

Nel caso in cui qualcuno stia cercando una soluzione in vanilla js che funzioni su IOS> 11.2 e non richieda alcun CSS aggiuntivo:

(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
    }
})()

Quello che fa è:

  1. Controlla se il browser è Safari su iOS 11.0.0 - 11.2.5
  2. Ascolta eventuali focusineventi sulla pagina
  3. Se l'elemento focalizzato è un inputo a textareaed è contenuto in un elemento con fixedposizione, modificare la posizione del contenitore in absolutementre si occupa scrollTope le dimensioni originali dei contenitori.
  4. In caso di sfocatura, ripristina la posizione del contenitore su fixed.

-1

Questa soluzione ha funzionato per me e funziona bene su tutti i browser su 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

Sostituisci css modale e cambia il suo positionda fixedaabsolute

.modal {
position: absolute !important;
}

1
Non ha funzionato nel mio scenario. Il passaggio da fisso ad assoluto può avere MOLTI effetti a catena
Steve D

@SteveD - per farlo funzionare dovresti aggiungere il tuo modale a <body>. E il <body> dovrebbe avere - position: relative. E quando dovrebbe funzionare :)
Dan

-3

aggiungi alla #modal position: absolute risolve problemi futuri legati alla posizione: fixed


La tua risposta è un duplicato esatto. Assicurati di non pubblicare risposte duplicate.
MechMK1
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.