Модальная текстовая область начальной загрузки Safari iOS 11 вне курсора

85

В сафари iOS 11 курсор текстового поля ввода находится за пределами текстового поля ввода. Мы не поняли, почему у него такая проблема. Как видите, мое сфокусированное текстовое поле - это ввод текста электронной почты, но мой курсор находится за его пределами. Это происходит только с iOS 11 Safari.

Проблема

Кеккеме
источник
1
Это ошибка Safari. Apple исправила это внутри, но исправления пока нет в публичной (или даже бета-версии) версии iOS. bugs.webkit.org/show_bug.cgi?id=176896
Beetle

Ответы:

69

Я исправил проблему, добавив position:fixedв тело при открытии модального окна. Надеюсь, что это поможет вам.

Наттават Тарвесрипаяп
источник
6
Примечание к ответам ниже - Bootstrap добавляет .modal-open в тело, когда модальное окно открыто, поэтому вы можете просто добавить position: fixed к этому классу.
timmyc
1
Вам также может потребоваться добавить, width:100%чтобы ограничить ширину корпуса шириной устройства. В некоторых случаях, в зависимости от существующей разметки, это может быть проблемой. Мне также нравится решение @gentleboy (ниже), чтобы не оштрафовать другие браузеры без проблем, потому что при установке тела в фиксированное значение тело прокручивается вверх, что несколько раздражает.
Redtopia 02
Как ни странно, у меня возникла эта проблема с сборкой приложения с помощью meteor-cordova. Похоже, что у любого устройства iOS с v11 + была эта проблема. В моем случае я уже 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>

Nota bene Функция 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

Micaball
источник
2
Хороший совет. У меня нет этой проблемы, но я думаю, что это может быть глобальный ответ. Я добавлю его после того, как допью пиво
micaball
3
@gentleboy Почему бы не использовать REGEX, чтобы найти любую OS 11? Таким образом, вам не придется обновлять каждое обновление OS 11 для вашего кода? что-то вроде этогоiOS11 = /OS 11_(\d{1,2})(_{0,1})(\d{1,2})/.test(us);
T. Evans
1
@RonakK, похоже, он все еще присутствует в бета- версиях
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 нет проблемы.
hackingchemist
11

Ошибка, спасибо за ее обнаружение. В противном случае я бы ударился айфоном или головой о стену.

Самое простое исправление (изменение 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 03
1
Некоторое время я занимаюсь этим вопросом. Это решение для меня неадекватно, поскольку добавление фиксированной позиции прокручивает страницу обратно наверх. Поэтому, когда пользователь закрывает модальное окно, он находится в другой позиции прокрутки. Это приводит к ужасному пользовательскому опыту
Nearpoint
Это исправление сработало для меня при использовании Chrome на мобильных устройствах. Сначала был сбит с толку, так как .modal-open не отображается в модальном html, но я все равно добавил его как CSS в свой заголовок, и он сработал.
Дэвид
4

Самое простое / чистое решение:

body.modal-open { position: fixed; width: 100%; }
lfkwtz
источник
1
Это самое простое решение, и оно работает, но единственная проблема в том, что курсор вообще пропадает. Это не так плохо, как исходная ситуация, но есть проблема с удобством использования.
tmo256
Странно, но я не
видел
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 plus это исправление приводит к тому, что мой курсор полностью исчезает
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.topwhen mounting. Это делается с использованием YOffsetзначения, полученного ygapпеременной.

Оттуда просто нужно сбросить body's style.topдо 0и переформатировать представление пользователя, используя window.scrollTo(0, ygap)when 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

Если кто-то ищет исправление в vanilla 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или, 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

Вы пробовали viewport-fit = cover для мета-окна просмотра.

Посмотрите на это: https://ayogo.com/blog/ios11-viewport/

Онклерогер
источник
Боюсь, это никак не влияет на рассматриваемую ошибку.
Ермо Ламерс
-2

Переопределите модальный CSS и измените его positionс fixedнаabsolute

.modal {
position: absolute !important;
}
Дэн
источник
1
Не сработало в моем сценарии. Изменение фиксированного значения на абсолютное может иметь
Стив Д.
@SteveD - чтобы он заработал, вы должны добавить модальное окно в <body>. И у <body> должно быть - position: relative. И когда это должно сработать :)
Дэн
-3

добавить в #modal position: absolute - исправить будущие проблемы, связанные с положением: fixed

Карла
источник
Ваш ответ - точная копия. Не публикуйте повторяющиеся ответы.
MechMK1