Avec iOS 11 safari, le curseur de la zone de saisie est en dehors de la zone de saisie. Nous n'avons pas compris pourquoi il avait ce problème. Comme vous pouvez le voir, ma zone de texte ciblée est la saisie de texte par courrier électronique, mais mon curseur est en dehors de celui-ci. Cela ne se produit qu'avec iOS 11 Safari
J'ai résolu le problème en ajoutant position:fixed
au corps lors de l'ouverture d'un modal . J'espère que cela vous aidera.
Personnellement, position: fixed
faites défiler vers le haut automatiquement. Assez ennuyeux!
Pour éviter de pénaliser les autres périphériques et versions, j'applique ce correctif uniquement aux versions appropriées d'iOS.
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%; }
J'ai modifié la fonction pour qu'elle ne se déclenche que pour les modaux sélectionnés avec une classe .inputModal
Seuls les modaux avec des entrées doivent être affecté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-invoquante
Depuis iOS 11.3, le bogue a été corrigé. Il n'est pas nécessaire de tester OS 11_
dans iOS11 = /OS 11_0|OS 11_1|OS 11_2/.test(ua);
Mais soyez prudent car iOS 11.2 est encore largement utilisé (à partir d'avril 2018). Voir
Ce problème dépasse le cadre de Bootstrap et ne concerne pas que Safari. C'est un bug 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
Soi-disant, ils l'ont déjà signalé à Apple comme un bug.
Bogue frustrant, merci de l'identifier. Sinon, je cognerais mon iphone ou ma tête contre le mur.
Le correctif le plus simple est (1 ligne de changement de code):
Il suffit d’ajouter le code CSS suivant au code 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">×</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
La solution la plus facile/la plus propre:
body.modal-open { position: fixed; width: 100%; }
Ce problème ne peut plus être reproduit après la mise à jour de vos appareils Apple vers iOS 11.3
Ajoutez position: fixed;
à body
quand 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">×</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>
Ces solutions utilisant position: fixed
et la correction de position basée sur scrollTop
fonctionnent très bien, mais certaines personnes (dont moi) ont un autre problème: le curseur/curseur du clavier ne s'affiche pas lorsque les entrées sont focalisées.
J'ai observé que le curseur/curseur ne fonctionne que lorsque NE PAS utiliser position: fixed
sur le corps. Donc, après avoir essayé plusieurs choses, j'ai renoncé à utiliser cette approche et décidé d'utiliser position: relative
sur body
et d'utiliser scrollTop
pour corriger la première position de modal.
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);
});
Comme mentionné précédemment: définir le style.position
property
de body
sur fixed
résout le problème iOS cursor misplacement
.
Cependant, ce gain se fait au prix d’un défilement forcé vers le haut de la page.
Heureusement, ce nouveau problème UX
peut être annulé sans trop de charge en utilisant HTMLElement.style
et window.scrollTo()
.
Le Gist de base consiste à contrecarrer le scroll to top
en manipulant le style.top
lorsque body
de l'élément mounting
. Ceci est fait en utilisant la valeur YOffset
capturée par la variable ygap
.
À partir de là, il suffit simplement de réinitialiser le body's
style.top
en 0
et de recadrer la vue de l'utilisateur à l'aide de window.scrollTo(0, ygap)
quand 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)
}