Обратите внимание, что эта запись в блоге была опубликована в июне 2015 года, поэтому в зависимости от того, когда вы её читаете, некоторые её части могут быть устаревшими. К сожалению, я не всегда могу поддерживать эти записи в актуальном состоянии, чтобы гарантировать точность информации.
- Моя тестовая страница
- Понимание поведения по умолчанию
- Понимание того, что делает CSS-правило "-webkit-overflow-scrolling"
- Пользователь должен подождать, пока закончится эффект упругого отскока
- Как предотвратить прокрутку фоновой страницы
- Как предотвратить лишнюю прокрутку фоновой страницы
- Как предотвратить перепрокрутку к фоновой странице
- Полный исходный код страницы
Чтобы исправить недавнюю проблему с прокруткой в Safari на iOS, мне пришлось создать собственную простую тестовую страницу и затем применить всю информацию и комбинации, которые мне удалось найти в интернете, чтобы попробовать устранить проблему.
Ниже приведён краткий обзор того, что я обнаружил в ходе этого процесса.
Моя тестовая страница
Моя тестовая страница была довольно базовой. Она состояла из фиктивных данных в статическом div, чтобы представить «страницу», вместе с фиксированным div, представляющим наложенное меню поверх страницы, вот так.
Полный исходный код этой страницы можно найти внизу этой записи в блоге.
Понимание поведения по умолчанию
Первым делом я попытался понять поведение Safari по умолчанию на iOS. Самым заметным наблюдением было то, что когда прокручивали страницу до самого низа меню, тело страницы демонстрировало «эффект резиновой ленты» — зашитая в Safari функция.
То же самое произошло, когда прокручивали меню до самого верха.
Однако первой проблемой стало то, что если непрерывно прокручивать до самого верха или низа, Safari начинает проявлять проблемы с отрисовкой как на фоновой странице, так и в меню.
Не уверен, почему так получается, но подозреваю, что это связано с эффектом отскока.
Понимание того, что делает CSS-правило "-webkit-overflow-scrolling"
Далее я применил правило CSS -webkit-overflow-scrolling к меню, которое обеспечивает прокрутку с инерцией.
Как показано на изображении ниже, прокрутка продолжается даже после того, как я провожу пальцем по экрану.
Но он также добавляет эффект резиновой ленты при прокрутке до самого верха или низа, как показано ниже.
Пользователь должен подождать, пока закончится эффект упругого отскока
Во время тестирования я также заметил(а), что пока идёт эффект отскока, пользователь не может перевести фокус на другой элемент до полного завершения анимации.
В примере ниже я сначала прокручиваю фоновую страницу, а затем быстро пытаюсь прокрутить меню, но в итоге продолжаю прокручивать фоновую страницу.
Однако, если подожду немного, пока эффект завершится, я смогу начать прокручивать меню.
Как предотвратить прокрутку фоновой страницы
Во время тестирования я также заметил, что пока идет эффект резиновой ленты, пользователь не может перевести фокус на другой элемент до полного завершения анимации.
Основано на этом ответе на Stack Overflow, вы можете обеспечить, что элементы с классом disable-scrolling не выполняют действие прокрутки по умолчанию при срабатывании события touchmove.
document.ontouchmove = function(event) {
var isTouchMoveAllowed = true,
target = event.target;
while (target !== null) {
if (target.classList && target.classList.contains('disable-scrolling')) {
isTouchMoveAllowed = false;
break;
}
target = target.parentNode;
}
if (!isTouchMoveAllowed) {
event.preventDefault();
}
};
Затем примените класс disable-scrolling к элементу div на странице:
<div class="page disable-scrolling">
Такой метод работает в целом, но при более обширном тестировании выясняется, что фоновая страница иногда может прокручиваться, когда вы достигаете самого верха или низа меню.
Как предотвратить лишнюю прокрутку фоновой страницы
Применив это исправление, можно предотвратить «перепрокрутку». Это означает, что когда элемент прокручивается до самого верха или низа, прокрутка не продолжится в тело документа.
В результате прокрутка тела страницы отключена, что также исключает возникновение эффекта резиновой прокрутки.
function removeIOSRubberEffect(element) {
element.addEventListener("touchstart", function() {
var top = element.scrollTop,
totalScroll = element.scrollHeight,
currentScroll = top + element.offsetHeight;
if (top === 0) {
element.scrollTop = 1;
} else if (currentScroll === totalScroll) {
element.scrollTop = top - 1;
}
});
}
removeIOSRubberEffect(document.querySelector(".scrollable"));
Этот фрагмент JavaScript не позволяет элементу добираться до самого верха или низа, удерживая его на расстоянии всего одного пикселя.
Если положение прокрутки никогда не достигает абсолютной верхней или нижней границы, перепрокрутка невозможна.
Как предотвратить перепрокрутку к фоновой странице
Если вы хотите избавиться от эффекта упругой прокрутки в меню — либо по эстетическим причинам, либо потому что, как и в моём случае, он вызывал дополнительные глюки при отрисовке, просто удалите CSS-правило -webkit-overflow-scrolling.
Однако, как видно ниже, вы также теряете плавность прокрутки.
Полный исходный код страницы
Вот полный исходный код этой страницы.
<!-- License MIT, Author Special Agent Squeaky (specialagentsqueaky.com) --> <!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="minimum-scale=1.0, width=device-width, maximum-scale=1.0, user-scalable=no, initial-scale=1">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style> .page{ font-size: 24px; overflow: scroll; } .menu{ position: fixed; top: 0; bottom: 0; left: 0; width: 80%; background: gray; z-index: 1; font-size: 10px; overflow: scroll; /* uncomment to get smooth momentum scroll, but also a rubber band effect */ /*-webkit-overflow-scrolling: touch;*/ } .menu-item{ padding: 10px; background: darkgray; font-size: 24px; } </style>
</head>
<body>
<div class="menu scrollable">
<div class="menu-item">hello world</div>
<div class="menu-item">hello world</div>
<div class="menu-item">hello world</div>
<div class="menu-item">hello world</div>
<div class="menu-item">hello world</div>
<div class="menu-item">hello world</div>
<div class="menu-item">hello world</div>
<div class="menu-item">hello world</div>
<div class="menu-item">hello world</div>
<div class="menu-item">hello world</div>
<div class="menu-item">hello world</div>
<div class="menu-item">hello world</div>
<div class="menu-item">hello world</div>
<div class="menu-item">hello world</div>
<div class="menu-item">hello world</div>
<div class="menu-item">hello world</div>
<div class="menu-item">hello world</div>
<div class="menu-item">hello world</div>
</div>
<div class="page disable-scrolling">
Lorem Ipsum is simply dummy text of the printing and typesetting industry.
Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,
when an unknown printer took a galley of type and scrambled it to make a type
specimen book. It has survived not only five centuries, but also the leap into
electronic typesetting, remaining essentially unchanged. It was popularised in
the 1960s with the release of Letraset sheets containing Lorem Ipsum passages,
and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
</div>
<script>
document.ontouchmove = function(event) {
var isTouchMoveAllowed = true,
target = event.target;
while (target !== null) {
if (target.classList && target.classList.contains('disable-scrolling')) {
isTouchMoveAllowed = false;
break;
}
target = target.parentNode;
}
if (!isTouchMoveAllowed) {
event.preventDefault();
}
};
function removeIOSRubberEffect(element) {
element.addEventListener("touchstart", function() {
var top = element.scrollTop,
totalScroll = element.scrollHeight,
currentScroll = top + element.offsetHeight;
if (top === 0) {
element.scrollTop = 1;
} else if (currentScroll === totalScroll) {
element.scrollTop = top - 1;
}
});
}
removeIOSRubberEffect(document.querySelector(".scrollable"));
</script>
</body>
</html>