Zone de texte modale d'amorçage iOS 11 Safari en dehors du curseur

85

Avec iOS 11 Safari, le curseur de la zone de texte d'entrée se trouve en dehors de la zone de texte d'entrée. Nous n'avons pas compris pourquoi il a ce problème. Comme vous pouvez le voir, ma zone de texte ciblée est une saisie de texte d'e-mail, mais mon curseur est en dehors de celle-ci. Cela ne se produit qu'avec iOS 11 Safari

Problème

kekkeme
la source
1
Il s'agit d'un bug avec Safari. Apple l'a corrigé en interne, mais le correctif n'est pas encore disponible dans une version publique (ni même bêta) d'iOS. bugs.webkit.org/show_bug.cgi?id=176896
Beetle

Réponses:

69

J'ai résolu le problème en ajoutant position:fixedau corps lors de l'ouverture d'un modal. J'espère que cela vous aidera.

Nattawat Tarweesripayap
la source
6
Note par réponses ci-dessous - Bootstrap ajoute .modal-open au body lorsque le modal est ouvert, vous pouvez donc simplement ajouter position: fixed à cette classe.
timmyc
1
Vous devrez peut-être également ajouter width:100%pour contraindre la largeur du corps à la largeur de l'appareil. Dans certains cas, en fonction du balisage existant, cela peut poser problème. J'aime aussi la solution de @gentleboy (ci-dessous) pour ne pas pénaliser les autres navigateurs sans problème, car lors de la définition du corps sur fixe fait défiler le corps vers le haut, ce qui est un peu gênant.
Redtopia
Étrangement, j'ai eu ce problème avec une application construite avec meteor-cordova. Il semblait que tout appareil iOS exécutant v11 + avait ce problème. Dans mon cas, j'avais déjà position:fixedpostulé au corps de la demande. Au lieu de cela, je l'ai changé position:absolutesur l' htmlélément et cela a résolu mon problème. Merci Jen!
Adam Ross Bowers
1
Cela pourrait être la fin des formulaires modaux pour nous. Même si Apple résout le problème, il appartiendra alors à l'utilisateur de mettre à jour son appareil pour voir le formulaire de travail. N'y a-t-il pas un moyen universel plus simple de résoudre ce problème? Et un remplissage poly?
Reado
1
vous m'avez sauvé la vie <3
Cristian B.
42

Personnellement, position: fixed faites défiler vers le haut automatiquement . Assez ennuyeux!

Pour éviter de pénaliser d'autres appareils et versions, j'applique ce correctif uniquement aux versions appropriées d'iOS.


** VERSION 1 - Tous les modaux corrigent **

Pour le 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");

    }

});

Pour le CSS

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

** VERSION 2 - Modaux sélectionnés uniquement **

J'ai modifié la fonction pour ne déclencher que pour les modaux sélectionnés avec une classe .inputModal

Seuls les modaux avec des entrées doivent être impactés pour éviter le défilement vers le haut.

Pour le 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);
                }
            });

        }
    })();
});

Pour le CSS

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

Pour le HTML Ajoutez la classe inputModal au modal

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

Nota bene La fonction javascript est maintenant auto-appelante


** MISE À JOUR iOS 11.3 - Bug corrigé 😃🎉 **

Depuis iOS 11.3, le bogue a été corrigé. Il n'y a pas besoin de test OS 11_dansiOS11 = /OS 11_0|OS 11_1|OS 11_2/.test(ua);

Mais soyez prudent car iOS 11.2 est toujours largement utilisé (en avril 2018). Voir

stat 1

stat 2

micaball
la source
2
Bon conseil. Je n'ai pas ce problème, mais je suppose que cela pourrait être une réponse globale. Je l'ajouterai après avoir fini mes bières
micaball
3
@gentleboy Pourquoi ne pas utiliser un REGEX pour trouver n'importe quel OS 11? De cette façon, vous n'auriez pas à mettre à jour chaque mise à jour OS 11 avec votre code? quelque chose comme çaiOS11 = /OS 11_(\d{1,2})(_{0,1})(\d{1,2})/.test(us);
T.Evans
1
@RonakK il semble qu'il soit toujours présent dans la version bêta 11.2 et 11.3 selon bugs.webkit.org
micaball
2
@ T.Evans que l'expression régulière ne fonctionne pas. Une regex correcte pourrait être quelque chose comme ceci:ios11 = /OS 11_(\d{1,2})/.test(ua);
Abraham
2
La position fixe fera toujours défiler le document / corps vers le haut. Je suggère de saisir la position actuelle de scrollTop sur l'ouverture modale et la valeur de scrollTop readd une fois le mode fermé. Comme ça jsfiddle.net/im4aLL/hmvget9x/1
HADI
15

Ce problème va au-delà de Bootstrap et au-delà de Safari. Il s'agit d'un bogue d'affichage complet dans iOS 11 qui se produit dans tous les navigateurs. Le correctif ci-dessus ne résout pas ce problème dans tous les cas.

Le bug est rapporté en détail ici:

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

Apparemment, ils l'ont déjà signalé à Apple comme un bogue.

Eric Shawn
la source
Cela semble être le problème pour moi, cela fonctionnait bien avant la mise à jour vers IOS 11. Les utilisateurs d'Android n'ont pas le problème.
hackingchemist
11

Bug frustrant, merci de l'avoir identifié. Sinon, je frapperais mon iPhone ou ma tête contre le mur.

La solution la plus simple est (1 ligne de changement de code):

Ajoutez simplement le CSS suivant au html ou à un fichier css externe.

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

Voici un exemple de travail complet:

.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>

J'ai soumis un problème ici: https://github.com/twbs/bootstrap/issues/24059

Scott David Murphy
la source
Cela devrait être la réponse. Bootstrap ajoute la classe modale ouverte au corps lorsqu'un modal est visible. Vous avez juste besoin de cibler cette classe.
lfkwtz
1
Je m'occupe de ce problème depuis un certain temps. Cette solution ne me convient pas car l'ajout d'une position fixe fait défiler la page vers le haut. Ainsi, lorsque l'utilisateur ferme le modal, il se trouve à une position de défilement différente. Cela conduit à une expérience utilisateur terrible
Nearpoint
Ce correctif a fonctionné pour moi en utilisant Chrome sur mobile. A été confus au début car .modal-open n'apparaît pas dans le html modal, mais je l'ai quand même ajouté en CSS dans mon en-tête et cela a fonctionné.
David
4

Solution la plus simple / la plus propre:

body.modal-open { position: fixed; width: 100%; }
lfkwtz
la source
1
C'est la solution la plus simple et cela fonctionne, mais le seul problème est que le curseur disparaît complètement. Ce n'est pas aussi grave que la situation initiale, mais il y a un problème de convivialité.
tmo256
Bizarre, je n'ai pas
vu de
1
J'ai également expérimenté le curseur qui disparaissait, des pensées sur pourquoi cela pourrait arriver?
Alex Burgos
4
Ouais, je vois aussi un curseur qui disparaît pour le premier événement de mise au point. Les foyers d'entrée suivants font apparaître le curseur. Très étrange. Cela ne se produit que dans Safari sur iOS.
Devin Walker
@DevinWalker avez-vous déjà résolu le problème du curseur qui disparaît?
accorder le
3

Ce problème n'est plus reproductible après la mise à jour de vos appareils Apple vers iOS 11.3

Eashan
la source
Que voulez-vous dire, Apple l'a corrigé?
Starscream
1
@Starscream oui c'est corrigé par Apple avec cette version du logiciel (iOS 11.3)
Eashan
2

Ajouter position: fixed;à bodylorsque modal est ouvert.

$(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>

Anuruk S.
la source
1
Semblable à d'autres solutions sur ce fil, une fois le modal fermé, vous êtes de retour au sommet. Par exemple: l'utilisateur fait défiler la grille des éléments, lance les détails dans un modal .. et avec ce correctif, lorsqu'il ferme le modal .. il est à nouveau en haut de la grille des éléments et doit re-faire défiler
bkwdesign
2
sur mon iPhone 7 plus, ce correctif fait disparaître complètement mon curseur
bkwdesign
1

Ces solutions utilisant position: fixedet la correction de position basée sur scrollTopfonctionnent très bien, mais certaines personnes (y compris moi) ont un autre problème: le curseur / curseur du clavier ne s'affiche pas lorsque les entrées sont ciblées.

J'ai observé que le caret / curseur ne fonctionne que lorsque nous ne l' utilisons PASposition: fixed sur le corps. Donc , après avoir essayé plusieurs choses, j'ai renoncé à utiliser cette approche et a décidé d'utiliser position: relativesur bodyet à l' utilisation scrollTopde bonne position en haut de modal à la place.

Voir le code ci-dessous:

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);
});
FlavioEscobar
la source
0

Comme mentionné précédemment: définir le style.position propertysur bodypour fixedrésout le iOS cursor misplacementproblème.

Cependant, ce gain se fait au prix d'un défilement forcé vers le haut de la page.

Heureusement, ce nouveau UXproblème peut être annulé sans trop de frais généraux en exploitant HTMLElement.styleet window.scrollTo().

L'essentiel est de contrecarrer le scroll to topen manipulant le moment de l' bodyélément . Ceci est fait en utilisant la valeur capturée par la variable.style.topmountingYOffsetygap

À partir de là, il s'agit simplement de réinitialiser le body's style.topvers 0et de recadrer la vue de l'utilisateur en utilisant window.scrollTo(0, ygap)when dismounting.

Voir ci-dessous pour un exemple pratique.

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

}
Arman Charan
la source
Avez-vous un exemple plus détaillé pour cela? J'imagine que j'aurais le Mount dans une fonction qui est appelée lorsque le modal s'ouvre, puis que je mettrais le Dismount dans une fonction et que je l'appellerais lorsque le modal est fermé.
Neal Jones
Aahh ouais. Je vois ce que tu veux dire. Veuillez voir ci-dessus pour une solution révisée. Aussi; oui, l'intention est que vous les appeliez functionsquand mountinget dismounting. Merci.
Arman Charan
-1

Incase quiconque cherche un correctif dans vanilla js qui fonctionne sur IOS> 11.2 et ne nécessite aucun CSS supplémentaire:

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

Ce que cela fait est:

  1. Vérifiez si le navigateur est Safari sur iOS 11.0.0 - 11.2.5
  2. Écoutez les focusinévénements sur la page
  3. Si l'élément ciblé est un inputou un textareaet est contenu dans un élément avec la fixedposition, changer la position du conteneur absoluteen ce qui concerne scrollTopet les conteneurs de dimensions d' origine.
  4. En cas de flou, restaurez la position du conteneur sur fixed.
Manuel Otto
la source
-1

Cette solution a fonctionné pour moi et fonctionne bien sur tous les navigateurs sur 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');
    }
});
Afzaal Khalid
la source
-2

Avez-vous essayé viewport-fit = cover pour la fenêtre méta.

Regardez ceci: https://ayogo.com/blog/ios11-viewport/

Oncleroger
la source
Cela n'a aucun effet sur le bogue en question, j'en ai peur.
Yermo Lamers
-2

Remplacer le css modal et changer son positionde fixedàabsolute

.modal {
position: absolute !important;
}
Dan
la source
1
Cela n'a pas fonctionné dans mon scénario. Changer de fixe en absolu peut avoir de nombreux effets sur les effets
Steve D
@SteveD - pour que cela fonctionne, vous devez ajouter votre modal à <body>. Et le <body> devrait avoir - position: relative. Et quand ça devrait marcher :)
Dan
-3

ajouter à la position #modal: absolu il corrige les futurs problèmes liés à la position: corrigé

Karla
la source
Votre réponse est une copie exacte. Veuillez vous assurer de ne pas publier de réponses en double.
MechMK1