Запретить прокрутку переполнения/резиновой ленты на iOS

Уже есть несколько вопросов по теме прокрутки переполнения/резиновой ленты на SO, но

  • ни одно из них не обеспечивает решение, работающее во всех случаях на iOS 9.3.2
  • ни одна из них не дает исчерпывающей и полной информации о самой проблеме.

поэтому я создал этот пост как совокупность знаний.


Проблема:

Вещь, о которой никогда не упоминалось ни в одной другой записи, заключается в том, что прокрутка переполнения iOS на самом деле является поведением двух частей.

1. Прокрутка содержимого с помощью overflow: auto/scroll

Это общеизвестное и в основном желаемое поведение элемента с -webkit-overflow-scrolling: touch, где непрерывное/импульсное поведение прокрутки проходит мимо контейнера элементов, чтобы плавно прокручивать прокручиваемый контент.

Это происходит, когда вы прокручиваете содержимое элемента с импульсом, достаточно высоким для прокрутки импульса, чтобы пройти мимо длины прокрученного содержимого.

При таком поведении свойство element.scrollTop изменяется соответственно на положение прокрутки элементов и меньше или больше, чем максимальная прокрутка (element.scrollHeight - element.offsetHeight).

2. Прокрутка переполнения <body>

Такое поведение возникает, если вы попытаетесь прокрутить любой элемент, уже находящийся в положении минимальной/максимальной прокрутки, даже дальше этого (элемент сверху вверх или элемент внизу вниз). Затем свиток, кажется, "пузырится" до тега <body> и прокручивается весь видовой экран.

В противоположность выше здесь свойство element.scrollTop не изменяется, а вместо него document.body.scrollTop.

Блокировка фокуса и переключение между режимами (задержка 1,5 с)

Наиболее раздражающим в этом контексте является то, что переключатель между двумя описанными выше типами не переключается мгновенно.

После ввода одного из них вы не можете переключать фокус на любой другой элемент (прокручиваемые элементы, кнопки, ссылки и т.д.), и поэтому поведение прокрутки также не изменяется.

Например:, если вы прокрутите элемент, уже находящийся в верхней позиции вверх, вы введете overflow scrolling type 2, и наиболее естественная реакция для пользователя - это попытаться прокрутить назад. Поскольку фокус заблокирован для прокрутки тела вместо перехода на overflow scrolling type 1, он остается в type 2, и все тело прокручивается вниз. Затем типичный пользователь начинает произвольно прокручивать вверх и вниз часто, не вырываясь из type 2.

Переключение фокуса и, таким образом, изменение поведения прокрутки происходит только после завершения анимации переполнения, и элемент стоит на месте (даже немного длиннее [около 0,5 с]).

таким образом, возвращаясь к предыдущему примеру, правильная реакция пользователя заключалась бы в том, чтобы перестать касаться экрана примерно 1 с - 1,5 с, а затем попытаться прокрутить вниз.

Ответ 1

Решение:

Тип 1:

Самое основное решение, предотвращающее прокрутку переполнения самого элемента, - это предотвращение дефолтов по событиям касания.

document.body.addEventListener('touchmove', function(e) { 
    e.preventDefault(); 
});

Однако этот метод отключает прокрутку собственных импульсов браузеров и, следовательно, не подходит для большинства приложений. Однако с некоторой доработкой (только для предотвращения прокрутки вверх или вниз прокрутки вниз...) этот метод устраняет большинство проблем. В этой записи SO можно найти множество возможных реализаций.

Тип 2:

Однако прокрутка переполнения на теле не предотвращается описанными выше способами.

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

anElement.addEventListener('touchstart', function( event ){
    if( this.scrollTop === 0 ) {
        this.scrollTop += 1;
    } else if( this.scrollTop + this.offsetHeight >= this.scrollHeight ) {
        this.scrollTop -= 1;
    }
}

Однако это не помогло работать над iOS 9.3.2.

Тем не менее, что работа была задана, position: fixed на элементе <body> чтобы предотвратить движение тела. Обратите внимание, однако, что это все еще не полностью останавливает type 2, поэтому иногда вы не можете прокручивать/фокусировать любой элемент, потому что в фоновом режиме type2 с блокировкой фокуса все еще происходит (опять же, когда вы перестаете касаться экрана на мгновение он снова работает так, как ожидалось).

Хотя это еще далеко не оптимальное решение, кажется, это лучшее, что мы можем получить за время, говорящее.

Изменить: Обратите внимание, что я не уверен, безопасно ли поставить position: fixed на элементе <body>. Чтобы отслеживать возможные проблемы, которые я создал после публикации SO. По-видимому, было бы лучше создать элемент оболочки как дочерний элемент тела и установить этот элемент в position: fixed чтобы избежать проблем с масштабированием.


Изменить 2: Определенное решение

Скрипт iNoBounce творит чудеса. Просто загрузите его на страницу и получите веб-приложение без отскока. До сих пор я не нашел никаких проблем с этим решением.