Pole tekstowe modalnego bootstrapu Safari w iOS 11 poza kursorem

85

W przypadku safari w systemie iOS 11 kursor pola tekstowego do wprowadzania tekstu znajduje się poza polem tekstowym. Nie zrozumieliśmy, dlaczego ma ten problem. Jak widać, moje skupione pole tekstowe to wprowadzanie tekstu przez e-mail, ale mój kursor jest poza nim. Dzieje się tak tylko w przypadku Safari na iOS 11

Problem

kekkeme
źródło
1
To jest błąd w Safari. Apple naprawiło to wewnętrznie, ale poprawka nie jest jeszcze w publicznej (ani nawet beta) wersji iOS. bugs.webkit.org/show_bug.cgi?id=176896
Beetle

Odpowiedzi:

69

Naprawiłem problem, dodając position:fixeddo ciała podczas otwierania modalu. Mam nadzieję, że to ci pomoże.

Nattawat Tarweesripayap
źródło
6
Uwaga na odpowiedzi poniżej - Bootstrap dodaje .modal-open do body, gdy modal jest otwarty, więc możesz po prostu dodać position: fixed do tej klasy.
timmyc
1
Może być również konieczne dodanie, width:100%aby ograniczyć szerokość treści do szerokości urządzenia. W niektórych przypadkach, w zależności od istniejących znaczników, może to stanowić problem. Podoba mi się również rozwiązanie @gentleboy (poniżej), aby bez problemu nie karać innych przeglądarek, ponieważ ustawienie body na fixed powoduje, że ciało przewija się do góry, co jest nieco denerwujące.
Redtopia
O dziwo, miałem ten problem z aplikacją zbudowaną z meteor-cordova. Wyglądało na to, że każde urządzenie iOS z systemem v11 + miało ten problem. W moim przypadku złożyłem już position:fixedwniosek do treści wniosku. Zamiast tego zmieniłem to position:absolutena htmlelement i rozwiązałem mój problem. Dziękuję Jen!
Adam Ross Bowers
1
To może być dla nas koniec form modalnych. Nawet jeśli Apple naprawi problem, użytkownik będzie musiał zaktualizować swoje urządzenie, aby zobaczyć działający formularz. Czy nie ma prostszego uniwersalnego sposobu, aby to naprawić? A co z wypełnieniem z tworzywa sztucznego?
Reado
1
uratowałeś mi życie <3
Cristian B.
42

Osobiście position: fixed przewiń do góry automatycznie . Dość irytujące !

Aby uniknąć karania innych urządzeń i wersji , stosuję tę poprawkę tylko do odpowiednich wersji iOS.


** WERSJA 1 - poprawka wszystkich modali **

W przypadku pliku 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");

    }

});

Dla CSS

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

** WERSJA 2 - tylko wybrane modały **

Zmodyfikowałem funkcję, aby uruchamiała się tylko dla wybranych modali z klasą .inputModal

Należy wpływać tylko na modały z danymi wejściowymi, aby uniknąć przewijania do góry.

W przypadku pliku 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);
                }
            });

        }
    })();
});

Dla CSS

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

Dla kodu HTML Dodaj klasę inputModal do pliku modal

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

Nota bene Funkcja javascript wywołuje się teraz automatycznie


** AKTUALIZACJA iOS 11.3 - Poprawiono błąd 😃🎉 **

Od iOS 11.3 błąd został poprawiony. Nie ma potrzeby testowania OS 11_wiOS11 = /OS 11_0|OS 11_1|OS 11_2/.test(ua);

Ale uważaj, ponieważ iOS 11.2 jest nadal szeroko stosowany (stan na kwiecień 2018). Widzieć

stat 1

stat 2

micaball
źródło
2
Niezła rada. Nie mam tego problemu, ale myślę, że to może być globalna odpowiedź. Dodam to po skończeniu piwa
micaball
3
@gentleboy Dlaczego nie skorzystać z REGEX, aby znaleźć dowolny system operacyjny 11? W ten sposób nie musiałbyś aktualizować każdej aktualizacji OS 11 do swojego kodu? coś takiegoiOS11 = /OS 11_(\d{1,2})(_{0,1})(\d{1,2})/.test(us);
T. Evans
1
@RonakK wygląda na to, że jest nadal obecny w wersji beta 11.2 i 11.3 według bugs.webkit.org
micaball
2
@ T.Evans, że wyrażenie regularne nie działa. Prawidłowe wyrażenie regularne może wyglądać ios11 = /OS 11_(\d{1,2})/.test(ua);
Abraham
2
Ustalona pozycja nadal spowoduje przewijanie dokumentu / treści na górę. Proponuję pobrać bieżącą pozycję scrollTop przy otwarciu modalnym i readd wartość scrollTop po zamknięciu modalnym. Podoba Ci się to jsfiddle.net/im4aLL/hmvget9x/1
Hadi
15

Ten problem wykracza poza Bootstrap i nie ogranicza się tylko do Safari. Jest to błąd pełnego wyświetlania w iOS 11, który występuje we wszystkich przeglądarkach. Powyższa poprawka nie rozwiązuje tego problemu we wszystkich przypadkach.

Błąd został szczegółowo zgłoszony tutaj:

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

Podobno już zgłosili to Apple jako błąd.

Eric Shawn
źródło
Wydaje się, że jest to dla mnie problem, działało dobrze przed aktualizacją do IOS 11. Użytkownicy Androida nie mają problemu.
hackingchemist
11

Frustrujący błąd, dzięki za zidentyfikowanie go. W przeciwnym razie uderzałbym iPhonem lub głową o ścianę.

Najprostsza poprawka to (zmiana 1 linii kodu):

Po prostu dodaj następujący CSS do html lub do zewnętrznego pliku css.

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

Oto pełny przykład działania:

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

Zgłosiłem problem tutaj: https://github.com/twbs/bootstrap/issues/24059

Scott David Murphy
źródło
To powinna być odpowiedź. Bootstrap dodaje klasę modal-open do ciała, gdy modal jest widoczny. Musisz tylko celować w tę klasę.
lfkwtz
1
Od jakiegoś czasu zajmuję się tą kwestią. To rozwiązanie jest dla mnie nieodpowiednie, ponieważ dodanie ustalonej pozycji powoduje przewinięcie strony z powrotem na górę. Więc kiedy użytkownik zamyka modal, znajdują się w innej pozycji przewijania. Prowadzi to do strasznego wrażenia użytkownika
Nearpoint
Ta poprawka zadziałała podczas korzystania z Chrome na urządzeniach mobilnych. Na początku byłem zdezorientowany, ponieważ .modal-open nie pojawia się w modalnym html, ale i tak dodałem go jako CSS w moim nagłówku i zadziałało.
David
4

Najłatwiejsze / najczystsze rozwiązanie:

body.modal-open { position: fixed; width: 100%; }
lfkwtz
źródło
1
To najłatwiejsze rozwiązanie i działa, ale jedynym problemem jest to, że kursor całkowicie znika. Nie jest tak źle, jak sytuacja początkowa, ale występuje problem z użytecznością.
tmo256
Dziwne, nie doświadczyłem znikającego kursora
lfkwtz
1
Doświadczyłem też znikającego kursora, jakieś myśli, dlaczego tak się stało?
Alex Burgos,
4
Tak, widzę też znikający kursor tylko dla pierwszego zdarzenia fokusu. Kolejne fokusy wprowadzania danych mają pojawiać się kursor. Bardzo dziwny. Dzieje się tak tylko w Safari na iOS.
Devin Walker
@DevinWalker Czy kiedykolwiek rozwiązaliście problem znikającego kursora?
udzielenie
3

Ten problem nie jest już odtwarzalny po zaktualizowaniu urządzeń Apple do iOS 11.3

Eashan
źródło
Co masz na myśli, że Apple to naprawiło?
Starscream
1
@Starscream tak, zostało to naprawione przez firmę Apple w tej wersji oprogramowania (iOS 11.3)
Eashan
2

Dodaj position: fixed;do bodykiedy modalna jest otwarta.

$(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.
źródło
1
Podobnie jak w przypadku innych rozwiązań w tym wątku, po zamknięciu modalu - znów jesteś na szczycie. Na przykład: użytkownik przewija siatkę pozycji, uruchamia szczegóły w trybie modalnym… i dzięki tej poprawce, kiedy zamyka modalnie… ponownie znajduje się na górze siatki elementów i musi ponownie przewijać
bkwdesign
2
na moim iPhonie 7 plus ta poprawka powoduje całkowite zniknięcie mojego kursora
bkwdesign
1

Te rozwiązania wykorzystujące position: fixedi oparte na korekcji pozycji scrollTopdziałają naprawdę dobrze, ale niektórzy ludzie (w tym ja) mają inny problem: kursor / kursor klawiatury nie wyświetla się, gdy dane wejściowe są skupione.

Zauważyłem, że daszek / kursor działa tylko wtedy, gdy NIE używamy position: fixedna ciele. Więc po wypróbowaniu kilku rzeczy zrezygnowałem z tego podejścia i zdecydowałem się zamiast tego użyć position: relativeon bodyi użyć scrollTopdo skorygowania górnej pozycji modalu .

Zobacz kod poniżej:

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
źródło
0

Jak już wspomniano: ustawienie style.position propertyof bodyna fixedrozwiązuje iOS cursor misplacementproblem.

Jednak zysk ten odbywa się kosztem przymusowego przewinięcia do góry strony.

Na szczęście ten nowy UXproblem można wyeliminować bez większych kosztów ogólnych, wykorzystując dźwignię HTMLElement.stylei window.scrollTo().

Podstawowym Istotą jest, aby przeciwdziałać scroll to toppoprzez manipulowanie bodyelementu style.topkiedy mounting. Odbywa się to za pomocą YOffsetwartości przechwyconej przez ygapzmienną.

Stamtąd wystarczy zresetować body's style.topdo 0i przeformułować widok użytkownika za pomocą window.scrollTo(0, ygap)kiedy dismounting.

Poniżej znajduje się praktyczny przykład.

// 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
źródło
Czy masz na to bardziej szczegółowy przykład? Domyślam się, że miałbym Mount w funkcji, która jest wywoływana, gdy modal się otwiera, a następnie umieściłbym Dismount w funkcji i wywołałby to, gdy modal jest zamknięty.
Neal Jones,
Ach tak. Rozumiem, co masz na myśli. Zobacz poprawione rozwiązanie powyżej. Również; tak, intencją jest, abyś dzwonił do nich, functionskiedy mountingi dismounting. Dzięki.
Arman Charan,
-1

O ile ktoś szuka poprawki w vanilla js, która działa na IOS> 11.2 i nie wymaga żadnego dodatkowego CSS:

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

Co to oznacza:

  1. Sprawdź, czy przeglądarka to Safari w systemie iOS 11.0.0 - 11.2.5
  2. Nasłuchuj focusinwydarzeń na stronie
  3. Jeśli skupiony element jest inputlub a textareai jest zawarty w elemencie z fixedpozycją, zmień położenie kontenera na absolutepodczas uwzględniania scrollTopi oryginalnych wymiarów kontenerów.
  4. Po rozmyciu przywróć pozycję kontenera do fixed.
Manuel Otto
źródło
-1

To rozwiązanie działało dla mnie i działa dobrze we wszystkich przeglądarkach na 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
źródło
-2

Czy spróbowałeś viewport-fit = cover dla meta rzutni.

Spójrz na to: https://ayogo.com/blog/ios11-viewport/

Oncleroger
źródło
Obawiam się, że nie ma to wpływu na omawiany błąd.
Yermo Lamers
-2

Zastąp modalne css i zmień jego wartość positionz fixednaabsolute

.modal {
position: absolute !important;
}
Dan
źródło
1
Nie zadziałało w moim scenariuszu. Zmiana stałej na absolutną może mieć WIELE efektów domina
Steve D
@SteveD - aby to zadziałało, należy dołączyć swój modal do <body>. Element <body> powinien mieć pozycję -: względną. A kiedy powinno działać :)
Dan
-3

dodaj do pozycji #modalnej: bezwzględnie napraw przyszłe problemy związane z pozycją: naprawiono

Karla
źródło
Twoja odpowiedź jest dokładnym duplikatem. Upewnij się, że nie publikujesz zduplikowanych odpowiedzi.
MechMK1