Vezměte prosím na vědomí, že tento blogový příspěvek byl publikován v červnu 2015, takže v závislosti na době, kdy jej čtete, mohou být některé části zastaralé. Bohužel nemohu tyto příspěvky vždy udržovat plně aktuální, aby informace zůstaly přesné.
- Moje testovací stránka
- Pochopení výchozího chování
- Chápání toho, co dělá CSS pravidlo "-webkit-overflow-scrolling"
- Uživatel musí počkat, než skončí efekt gumového pásu
- Jak zabránit posouvání pozadí stránky
- Jak zabránit nadměrnému posouvání stránky na pozadí
- Jak zabránit posouvání na pozadí stránky
- Celý zdrojový kód stránky
Abych vyřešil nedávný problém se scrollováním v Safari na iOS, byl jsem nucen vytvořit vlastní jednoduchou testovací stránku a poté využít všechny informace a kombinace, které jsem našel na internetu, abych se pokusil problém vyřešit.
Níže je stručný přehled toho, co jsem během tohoto procesu zjistil.
Moje testovací stránka
Moje testovací stránka byla poměrně jednoduchá. Skládala se z ukázkových dat ve statickém divu, který měl představovat „stránku“, a z fixně umístěného divu představujícího překryté „menu“ nad stránkou, takto to vypadá.
Celý zdrojový kód této stránky najdete na konci tohoto blogového příspěvku.
Pochopení výchozího chování
První věc, kterou jsem udělal, bylo zjistit výchozí chování Safari na iOS. Nejvýraznějším zjištěním bylo, že když jste se posunuli až na samotný konec nabídky, tělo stránky vykazovalo "rubber-band efekt" – pevně zabudovaná funkce v Safari.
To samé se stalo i při rolování až na úplný horní konec nabídky.
Nicméně první problém, který jsem si všiml, byl ten, že pokud neustále rolujete až na samotný vrchol nebo spodní konec, Safari začne vykazovat artefakty při vykreslování jak na pozadí stránky, tak v nabídce.
Nevím proč, ale mám podezření, že to souvisí s funkcí elastického efektu.
Chápání toho, co dělá CSS pravidlo "-webkit-overflow-scrolling"
Dále jsem na menu aplikoval pravidlo CSS -webkit-overflow-scrolling, které umožňuje plynulé posouvání s momentumem.
Jak je vidět na níže uvedeném obrázku, posouvání pokračuje i po přejetí prstem.
Ale při scrollování až na úplný vrchol nebo dolní konec stránky se objeví i efekt pružení, jak je níže uvedeno.
Uživatel musí počkat, než skončí efekt gumového pásu
Během testování jsem si také všiml, že zatímco probíhá efekt gumového pásku, uživatel nemůže přepnout fokus na jiný prvek, dokud se animace plně nedokončí.
V níže uvedeném příkladu nejprve posunu pozadí stránky a poté rychle zkusím posouvat menu, ale nakonec budu pokračovat v posouvání pozadí stránky.
Nicméně, pokud počkám chvíli, až efekt skončí, mohu pak začít procházet nabídku.
Jak zabránit posouvání pozadí stránky
Během testování jsem si také všiml, že zatímco probíhá elastický efekt, uživatel nemůže přepnout fokus na jiný prvek, dokud se animace zcela neukončí.
Na základě této odpovědi na Stack Overflow můžete zajistit, že prvky s disable-scrolling neprovedou výchozí akci posouvání, když dojde k vyvolání události 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();
}
};
A pak aplikujte třídu disable-scrolling na div stránky:
<div class="page disable-scrolling">
Takhle to sice funguje, ale při důkladnějším testování se ukazuje, že pozadí stránky se občas ještě posune, když dosáhnete na úplný horní nebo spodní konec nabídky.
Jak zabránit nadměrnému posouvání stránky na pozadí
Použitím tohoto opravného řešení je možné zabránit „overscrollování“. To znamená, že když je prvek posouván až na horní nebo dolní okraj, posouvání se nepřenáší do těla stránky.
Výsledkem je, že tělo stránky nelze posouvat, což také zabraňuje vzniku efektu odskoku.
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"));
Co dělá tento kousek JavaScriptu, je to, že zabraňuje prvku dostat se až na horní ani dolní okraj, a udržuje ho jen 1 pixel od těchto okrajů.
Pokud se posun nikdy nedostane na absolutní horní ani dolní okraj, nedojde k overscrollu.
Jak zabránit posouvání na pozadí stránky
Pokud chcete odstranit efekt elastického posouvání z nabídky – ať už z estetických důvodů, nebo protože, jak tomu bylo u mě, způsoboval další problémy s vykreslováním – jednoduše odstraňte pravidlo CSS -webkit-overflow-scrolling.
Avšak, jak je níže vidět, ztratíte i plynulost rolování.
Celý zdrojový kód stránky
Zde je úplný zdrojový kód stránky.
<!-- 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>