Área de texto modal de bootstrap do iOS 11 Safari fora do cursor


85

Com o iOS 11 safari, o cursor da caixa de texto de entrada está fora da caixa de texto de entrada. Não entendemos por que ele está tendo esse problema. Como você pode ver, minha caixa de texto em foco é a entrada de texto de e-mail, mas meu cursor está fora dela. Isso só acontece com iOS 11 Safari

Problema


1
Este é um bug do Safari. A Apple o corrigiu internamente, mas a correção ainda não está em uma versão pública (ou mesmo beta) do iOS. bugs.webkit.org/show_bug.cgi?id=176896
Beetle

Respostas:


69

Corrigi o problema adicionando position:fixedao corpo ao abrir um modal. Espero que isso ajude você.


6
Nota para as respostas abaixo - Bootstrap adiciona .modal-open ao corpo quando o modal está aberto, então você pode simplesmente adicionar position: fixed a essa classe.
timmyc

1
Você também pode precisar adicionar width:100%para restringir a largura do corpo à largura do dispositivo. Em alguns casos, dependendo da marcação existente, isso pode ser um problema. Também gosto da solução do @gentleboy (abaixo) para não penalizar outros navegadores sem o problema, pois ao definir o corpo para fixo faz com que o corpo role para cima, o que é um tanto chato.
Redtopia

Estranhamente, tive esse problema com um aplicativo construído com meteoro-cordova. Parecia que qualquer dispositivo iOS rodando v11 + tinha esse problema. No meu caso já havia me position:fixedinscrito no corpo da petição. Em vez disso, mudei para position:absoluteno htmlelemento e corrigiu meu problema. Obrigada Jen!
Adam Ross Bowers

1
Isso pode ser o fim dos formulários modais para nós. Mesmo se a Apple corrigir o problema, caberá ao usuário atualizar seu dispositivo para ver o formulário de trabalho. Não existe uma maneira universal mais fácil de consertar isso? Que tal um preenchimento de poliéster?
Reado

1
você salvou minha vida <3
Cristian B.

42

Pessoalmente, position: fixed role para cima automaticamente . Um tanto irritante !

Para evitar penalizar outros dispositivos e versões , aplico esta correção apenas às versões apropriadas do iOS.


** VERSÃO 1 - Correção em todos os modais **

Para o 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 o CSS

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

** VERSÃO 2 - Apenas modais selecionados **

Modifiquei a função para disparar apenas para modais selecionados com uma classe .inputModal

Apenas os modais com entradas devem ser impactados para evitar a rolagem para o topo.

Para o 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 o CSS

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

Para o HTML Adicione a classe inputModal ao modal

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

Nota bene A função javascript agora é auto-invocada


** ATUALIZAR iOS 11.3 - Bug corrigido 😃🎉 **

No iOS 11.3, o bug foi corrigido. Não há necessidade de testar OS 11_emiOS11 = /OS 11_0|OS 11_1|OS 11_2/.test(ua);

Mas tenha cuidado pois o iOS 11.2 ainda é amplamente usado (em abril de 2018). Vejo

stat 1

stat 2


2
Bom conselho. Não tenho esse problema, mas acho que isso pode ser uma resposta global. Vou adicioná-lo depois de terminar minhas cervejas
micaball

3
@gentleboy Por que não usar um REGEX para encontrar qualquer OS 11? Assim, você não teria que atualizar cada atualização do OS 11 em seu código? algo assimiOS11 = /OS 11_(\d{1,2})(_{0,1})(\d{1,2})/.test(us);
T. Evans

1
@RonakK parece que ainda está presente nas versões
micaball

2
@ T.Evans que regex não funciona. Um regex correto pode ser algo assim:ios11 = /OS 11_(\d{1,2})/.test(ua);
Abraham

2
A posição fixa ainda levará a rolagem do documento / corpo para o topo. Eu sugiro pegar a posição atual do scrollTop no modal aberto e ler o valor do scrollTop quando o modal for fechado. Curta jsfiddle.net/im4aLL/hmvget9x/1
HADI

15

Esse problema vai além do Bootstrap e não apenas do Safari. É um bug de exibição completa no iOS 11 que ocorre em todos os navegadores. A correção acima não corrige esse problema em todas as instâncias.

O bug é relatado em detalhes aqui:

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

Supostamente, eles já relataram à Apple como um bug.


Este parece ser o problema para mim, estava funcionando bem antes de atualizar para o IOS 11. Os usuários do Android não estão tendo o problema.
hackingchemist

11

Bug frustrante, obrigado por identificá-lo. Caso contrário, eu estaria batendo meu iphone ou minha cabeça contra a parede.

A correção mais simples é (1 linha de alteração de código):

Basta adicionar o seguinte CSS ao html ou a um arquivo css externo.

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

Aqui está um exemplo completo de trabalho:

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

Enviei um problema aqui: https://github.com/twbs/bootstrap/issues/24059


Esta deve ser a resposta. O bootstrap adiciona a classe aberta modal ao corpo quando um modal está visível. Você só precisa direcionar essa classe.
lfkwtz

1
Venho lidando com esse problema há algum tempo. Esta solução é inadequada para mim, pois adicionar posição fixa rola a página de volta ao topo. Portanto, quando o usuário fecha o modal, ele está em uma posição de rolagem diferente. Isso leva a uma experiência do usuário terrível
Nearpoint

Essa correção funcionou para mim ao usar o Chrome no celular. Fiquei confuso no início, pois .modal-open não aparece no html modal, mas eu adicionei mesmo assim como CSS no meu cabeçalho e funcionou.
David

4

Solução mais fácil / limpa:

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

1
Esta é a solução mais fácil e funciona, mas o único problema é que o cursor desaparece completamente. Não é tão ruim quanto a situação inicial, mas há um problema de usabilidade.
tmo256 de

Estranho, não experimentei um cursor desaparecendo
lfkwtz

1
Eu também experimentei o cursor desaparecendo, alguma ideia de por que isso poderia acontecer?
Alex Burgos

4
Sim, também estou vendo um cursor desaparecendo apenas para o primeiro evento de foco. Os focos de entrada subsequentes apresentam o cursor. Muito estranho. Isso só está acontecendo no Safari no iOS.
Devin Walker

@DevinWalker, vocês já resolveram o problema do desaparecimento do cursor?
concessão

3

Este problema não é mais reproduzível após a atualização de seus dispositivos Apple para iOS 11.3


O que você quer dizer com a Apple consertou?
Starscream,

1
@Starscream sim, foi corrigido pela Apple com esta versão de software (iOS 11.3)
Eashan

2

Adicione position: fixed;a bodyquando o modal estiver aberto.

$(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
Semelhante a outras soluções neste tópico, uma vez que o modal fecha - você está de volta ao topo novamente. Por exemplo: o usuário rola pela grade de itens, lança detalhes em um modal .. e com esta correção, quando fecha o modal .. eles estão mais uma vez no topo da grade de itens novamente e devem rolar
novamente

2
no meu iPhone 7 plus, essa correção faz com que meu cursor desapareça por completo
bkwdesign

1

Essas soluções que usam uma position: fixedcorreção de posição com base em scrollTopfuncionam muito bem, mas algumas pessoas (incluindo eu) tiveram outro problema: cursor / cursor do teclado não mostrado quando as entradas estão focadas.

Observei que acento circunflexo / cursor funciona apenas quando NÃO usamos position: fixedno corpo. Então, depois de tentar várias coisas, eu desisti de usar essa abordagem e decidiu uso position: relativeem bodye uso scrollToppara corrigir posição superior do modal vez.

Veja o código abaixo:

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 mencionado anteriormente: definir style.position propertyde bodycomo fixedresolve o iOS cursor misplacementproblema.

No entanto, esse ganho tem o custo de ser rolado à força para o topo da página.

Felizmente, esse novo UXproblema pode ser negado sem muita sobrecarga, aproveitando HTMLElement.stylee window.scrollTo().

A essência básica é neutralizar o scroll to top, manipulando o quando bodydo elemento . Isso é feito usando o valor capturado pela variável.style.topmountingYOffsetygap

A partir daí, é simplesmente uma questão de redefinir o body's style.toppara 0e reformular a visão do usuário usando window.scrollTo(0, ygap)quando dismounting.

Veja abaixo um exemplo prático.

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

}

Você tem um exemplo mais detalhado para isso? Estou supondo que eu teria o Mount em uma função que é chamada quando o modal é aberto e, em seguida, colocaria o Dismount em uma função e a chamaria quando o modal fosse fechado.
Neal Jones

Aahh sim. Eu vejo o que você quer dizer. Consulte acima para obter uma solução revisada. Além disso; sim, a intenção é que você ligue para aqueles functionsquando mountinge dismounting. Obrigado.
Arman Charan

-1

No caso de alguém estar procurando uma correção para o vanilla js que funciona no IOS> 11.2 e não requer nenhum 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
    }
})()

O que isso faz é:

  1. Verifique se o navegador é Safari no iOS 11.0.0 - 11.2.5
  2. Ouça todos os focusineventos na página
  3. Se o elemento em foco for um inputou a textareae estiver contido em um elemento com fixedposição, altere a posição do contêiner para absoluteenquanto em relaçãoscrollTop às dimensões originais dos contêineres.
  4. No desfoque, restaure a posição do contêiner para fixed.

-1

Essa solução funcionou para mim e está funcionando bem em todos os navegadores do 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

Substitua o css modal e altere-o positionde fixedparaabsolute

.modal {
position: absolute !important;
}

1
Não funcionou no meu cenário. Mudar de fixo para absoluto pode ter MUITOS efeitos de impacto
Steve D

@SteveD - para fazer funcionar você deve anexar seu modal a <body>. E o <body> deve ter - position: relative. E quando deveria funcionar :)
Dan

-3

adicionar à posição #modal: absoluto corrige problemas futuros relacionados à posição: corrigido


Sua resposta é uma duplicata exata. Por favor, certifique-se de não postar respostas duplicadas.
MechMK1
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.