Модальна текстова область iOS 11 Safari bootstrap поза курсором


85

У сафарі iOS 11 курсор введеного текстового поля знаходиться поза вхідним текстовим полем. Ми не зрозуміли, чому вона має цю проблему. Як бачите, моє зосереджене текстове поле - це введення тексту електронною поштою, але мій курсор знаходиться поза ним. Це відбувається лише з iOS 11 Safari

Проблема


1
Це помилка в Safari. Apple виправила це внутрішньо, але виправлення ще не в публічному (або навіть бета-версії) iOS. bugs.webkit.org/show_bug.cgi?id=176896
Жук,

Відповіді:


69

Я вирішив проблему, додавши position:fixedдо тіла при відкритті модального. Сподіваюся, це допоможе вам.


6
Примітка щодо відповідей нижче - Bootstrap додає .modal-open до тіла, коли модаль відкритий, тому ви можете просто додати position: fixed до цього класу.
timmyc

1
Можливо, вам також доведеться додати, width:100%щоб обмежити ширину корпусу шириною пристрою. У деяких випадках, залежно від існуючої розмітки, це може бути проблемою. Мені також подобається рішення @gentleboy (нижче), щоб не карати інші браузери без проблем, тому що при встановленні тіла фіксованого змушує тіло прокручуватись до верху, що дещо дратує.
Redtopia

Як не дивно, у мене виникла ця проблема зі складанням програми з метеором-кордовою. Здавалося, на будь-якому пристрої iOS, що працює в версії 11 +, була ця проблема. У моєму випадку я вже position:fixedзвертався до основного матеріалу заявки. Натомість я змінив його position:absoluteна htmlелемент, і це виправило мою проблему. Дякую Джен!
Адам Росс Бауерс

1
На цьому модальні форми для нас можуть закінчитися. Навіть якщо Apple вирішить проблему, користувач повинен оновити свій пристрій, щоб побачити робочу форму. Чи не існує простіший універсальний спосіб це виправити? А як щодо полі заливки?
Reado

1
ти врятував мені життя <3
Крістіан Б.

42

Особисто position: fixed прокрутіть до початку автоматично . Досить прикро!

Щоб уникнути покарання за інші пристрої та версії, я застосовую це виправлення лише до відповідних версій iOS.


** ВЕРСІЯ 1 - Виправлено всі способи **

Для 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");

    }

});

Для CSS

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

** ВЕРСІЯ 2 - Лише для вибраних модалів **

Я змінив функцію на запуск лише для вибраних модалів із класом .inputModal

Слід впливати лише на модалі з входами, щоб уникнути прокрутки вгору.

Для 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);
                }
            });

        }
    })();
});

Для CSS

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

Для HTML Додайте клас inputModal до модального

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

Примітка. Функція javascript тепер викликається самостійно


** ОНОВЛЕННЯ iOS 11.3 - Виправлено помилку 😃🎉 **

Починаючи з iOS 11.3, помилка була виправлена. Там немає необхідності тестування для OS 11_вiOS11 = /OS 11_0|OS 11_1|OS 11_2/.test(ua);

Але будьте обережні, оскільки iOS 11.2 все ще широко використовується (станом на квітень 2018 року). Подивитися

статистика 1

стати 2


2
Приємна порада. У мене немає цієї проблеми, але я думаю, що це може бути загальносвітовим рішенням. Я додаю його після того, як закінчу пиво
micaball

3
@gentleboy Чому б не використовувати REGEX, щоб знайти будь-яку ОС 11? Отже, таким чином вам не доведеться оновлювати кожне оновлення OS 11 до свого коду? приблизно такiOS11 = /OS 11_(\d{1,2})(_{0,1})(\d{1,2})/.test(us);
Т. Еванс

1
@RonakK, схоже, він все ще присутній у бета-версії 11.2 та 11.3 за даними bugs.webkit.org
micaball

2
@ T.Evans, що регулярний вираз не працює. Правильний регулярний вираз може бути приблизно таким:ios11 = /OS 11_(\d{1,2})/.test(ua);
Авраам

2
Позиція з фіксованим ключем все одно буде прокручувати документ / текст до початку. Я пропоную захопити поточну позицію scrollTop на модальному відкритті та прочитати значення scrollTop після модального закриття. Подобається це jsfiddle.net/im4aLL/hmvget9x/1
HADI

15

Ця проблема виходить за рамки Bootstrap та не лише Safari. Це повна помилка дисплея в iOS 11, яка виникає у всіх браузерах. Виправлення вище не вирішує цю проблему у всіх випадках.

Детально про помилку повідомляється тут:

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

Мовляв, вони вже повідомляли про це Apple як про помилку.


Здається, це проблема для мене, я працював нормально до оновлення до IOS 11. У користувачів Android проблеми не виникає.
хакерський хімік,

11

Неприємна помилка, дякую, що її визначили. В іншому випадку я б ударив iphone або головою об стіну.

Найпростішим виправленням є (1 рядок зміни коду):

Просто додайте наступний CSS до html або до зовнішнього файлу css.

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

Ось повний робочий приклад:

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

Я подав випуск тут: https://github.com/twbs/bootstrap/issues/24059


Це має бути відповіддю. Bootstrap додає клас відкритого модального тексту до тіла, коли видно модальний. Вам просто потрібно націлити цей клас.
lfkwtz

1
Я вже деякий час займаюся цим питанням. Це рішення для мене неадекватне, оскільки додавання фіксованого положення прокручує сторінку до початку. Отже, коли користувач закриває модальний режим, вони знаходяться в іншій позиції прокрутки. Це призводить до жахливого досвіду для користувачів
Nearpoint

Це виправлення спрацювало для мене при використанні Chrome на мобільному. Спочатку був заплутаний, оскільки .modal-open не відображається в модальному html, але я все одно додав його як CSS у свій заголовок, і він спрацював.
Девід

4

Найпростіше / найчистіше рішення:

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

1
Це найпростіше рішення, і воно працює, але єдина проблема - курсор взагалі зникає. Це не так погано, як початкова ситуація, але є проблема юзабіліті.
tmo256

Дивно, я не відчував зникнення курсору
lfkwtz

1
Я також відчув зникаючий курсор, будь-які думки про те, чому це може статися?
Alex Burgos

4
Так, я також бачу зникаючий курсор лише для першої події фокусування. Подальші фокуси введення мають курсор. Дуже дивно. Це відбувається лише в Safari на iOS.
Девін Уокер

@DevinWalker, ви, хлопці, коли-небудь вирішували проблему зникнення курсору?
грант

3

Ця проблема більше не відтворюється після оновлення пристроїв Apple до iOS 11.3


Що ви маєте на увазі, Apple це виправила?
Starscream

1
@Starscream так, це виправлено apple за допомогою цієї версії програмного забезпечення (iOS 11.3)
Ішан,

2

Додати position: fixed;в bodyколи модальний відкритий.

$(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
Подібно до інших рішень у цій темі, як тільки модаль закриється - ви знову повернетесь на вершину. Наприклад: користувач прокручує сітку елементів, запускає деталі в модальному режимі .. і з цим виправленням, коли вони закривають модальний .. вони знову опиняються у верхній частині сітки елементів і повинні повторно прокручувати
bkwdesign

2
на моєму iPhone 7 плюс це виправлення змушує мій курсор взагалі зникати
bkwdesign

1

Ці рішення, що використовують position: fixedі корекцію положення на основі scrollTopроботи, працюють дуже добре, але деякі люди (включаючи мене) мали ще одну проблему: клавіатура / курсор не відображаються, коли вводиться фокус.

Я помітив, що курсор / курсор працює лише тоді, коли ми НЕ використовуємо position: fixedтіло. Так після спроби кілька речей, я відмовився від використання цього підходу і вирішив використовувати position: relativeна bodyі використання , scrollTopщоб виправити верхнє положення модального в замість цього.

Дивіться код нижче:

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

Як уже згадувалося раніше: установка style.position propertyз bodyдо fixedВирішує iOS cursor misplacementпроблеми.

Однак цей виграш відбувається за рахунок примусового прокручування до початку сторінки.

На щастя, цю нову UXпроблему можна заперечити без особливих накладних витрат, використовуючи HTMLElement.styleі window.scrollTo().

Основна суть - протидіяти scroll to top, маніпулюючи bodyелементом style.topколи mounting. Це робиться з використанням YOffsetзначення, захопленого ygapзмінною.

Звідти це просто питання Перевстановлення body's style.topдо 0і рефреймінг зору користувача , використовуючи window.scrollTo(0, ygap)при dismounting.

Дивіться нижче практичний приклад.

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

}

Чи є у вас більш детальний приклад для цього? Я припускаю, що я мав би Mount у функції, яка викликається, коли модаль відкривається, а потім помістити Dismount у функцію і викликати це, коли модал закритий.
Ніл Джонс,

Ааа, так. Я розумію, що ти маєш на увазі. Будь ласка, зверніться до переглянутого рішення вище. Також; так, ви маєте намір зателефонувати тим, functionsколи mountingта dismounting. Дякую.
Арман Чаран,

-1

Якщо хтось шукає виправлення у ванільному js, яке працює на IOS> 11.2 і не вимагає додаткових 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
    }
})()

Що це робить:

  1. Перевірте, чи браузер Safari на iOS 11.0.0 - 11.2.5
  2. Слухайте будь-які focusinподії на сторінці
  3. Якщо сфокусований елемент - це inputабо a, textareaі він міститься в елементі з fixedположенням, змініть положення контейнера на absoluteчас відносно scrollTopта оригінальні розміри контейнерів.
  4. Після розмиття відновіть положення контейнера до fixed.

-1

Це рішення працювало для мене, і воно добре працює у всіх браузерах на 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

Перевизначте модальний CSS та змініть його positionз fixedнаabsolute

.modal {
position: absolute !important;
}

1
Не працював за моїм сценарієм. Зміна фіксованого значення на абсолютне може викликати БАГАТО ефектів
Стів Д.

@SteveD - щоб це працювало, слід додати свій модаль до <body>. А <body> повинен мати - position: relative. І коли це має спрацювати :)
Ден,

-3

додати до #modal position: absolute це виправити майбутні проблеми, пов'язані з позицією: fixed


Ваша відповідь - точна копія. Будь ласка, переконайтеся, що не публікуєте дублікатів відповідей.
MechMK1
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.