Vær oppmerksom på at dette blogginnlegget ble publisert i juni 2015, så avhengig av når du leser det, kan enkelte deler være utdaterte. Dessverre kan jeg ikke alltid holde disse innleggene helt oppdaterte for å sikre at informasjonen forblir nøyaktig.
- Min testside
- Forstå standardoppførsel
- Forstå hva CSS-regelen "-webkit-overflow-scrolling" gjør
- Brukeren må vente til gummibåndseffekten er ferdig.
- Slik hindrer du at bakgrunnssiden ruller
- Hvordan forhindre at bakgrunnssiden scroller for mye
- Slik hindrer du overscroll til bakgrunnssiden
- Hele sidens kildekode
For å fikse et nylig rulleproblem i Safari på iOS, ble jeg nødt til å lage min egen enkle testside og deretter bruke all informasjon og alle kombinasjonene jeg kunne finne på nettet for å prøve å løse problemet.
Nedenfor følger en kort oversikt over hva jeg har funnet ut i løpet av denne prosessen.
Min testside
Min testside var ganske enkel. Den besto av mock-data i en statisk div for å representere «siden», sammen med en fast div for å representere en overlagt «meny» over siden, slik.
Den komplette kildekoden til siden finner du nederst i dette blogginnlegget.
Forstå standardoppførsel
Det første jeg gjorde var å prøve å forstå standardoppførselen i Safari på iOS. Den mest bemerkelsesverdige observasjonen var at når du scrollet helt ned til bunnen av menyen, viste sidens innhold en "gummibånd-effekt" - en innebygd funksjon i Safari.
Det samme skjedde da jeg scrollet helt til toppen av menyen.
Imidlertid var det første problemet jeg la merke til at hvis du ruller kontinuerlig helt til toppen eller bunnen, begynner Safari å vise renderingsfeil i både bakgrunnssiden og menyen.
Jeg er ikke sikker på hvorfor, men jeg mistenker at det har noe å gjøre med gummibåndfunksjonen.
Forstå hva CSS-regelen "-webkit-overflow-scrolling" gjør
Det neste jeg gjorde var å bruke CSS-regelen -webkit-overflow-scrolling på menyen, som gir momentumbasert scrolling.
Som vist i bildet nedenfor, fortsetter rullingen selv om jeg sveiper med fingeren.
Men det gir også en gummibåndseffekt når du ruller helt til toppen eller bunnen, som vist nedenfor.
Brukeren må vente til gummibåndseffekten er ferdig.
Under testingen jeg gjennomførte la jeg også merke til at mens gummibåndseffekten pågår, kan brukeren ikke flytte fokus til et annet element før animasjonen er fullført.
I eksemplet nedenfor scroller jeg først på bakgrunnssiden og prøver deretter raskt å scrolle i menyen, men ender opp med å fortsette å scrolle på bakgrunnssiden i stedet.
Men hvis jeg venter et øyeblikk til effekten er ferdig, kan jeg deretter begynne å bla i menyen.
Slik hindrer du at bakgrunnssiden ruller
Under testingen min la jeg også merke til at mens gummibåndseffekten pågår, kan brukeren ikke flytte fokus til et annet element før animasjonen er helt ferdig.
Basert på dette svaret fra Stack Overflow, kan du sikre at elementer med disable-scrolling ikke utfører deres standard rulleeffekt når touchmove-hendelsen utløses.
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();
}
};
Og deretter bruker du klassen disable-scrolling på sidens div:
<div class="page disable-scrolling">
Dette fungerer greit, men mer omfattende tester viser at bakgrunnssiden fortsatt noen ganger kan rulle når du når øverste eller nederste del av menyen.
Hvordan forhindre at bakgrunnssiden scroller for mye
Ved å bruke denne løsningen, blir det mulig å forhindre «overscroll». Dette betyr at når et element rulles helt til toppen eller bunnen, fortsetter ikke rulningen inn i body-delen.
Som et resultat hindres siden fra å skrolle, noe som også hindrer at gummibåndseffekten oppstår.
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"));
Hva denne JavaScript-kodesnutten gjør, er å forhindre at elementet når helt opp til toppen eller bunnen ved å holde det bare 1 piksel unna.
Hvis rulleposisjonen aldri når den absolutte toppen eller bunnen, kan ikke overscroll forekomme.
Slik hindrer du overscroll til bakgrunnssiden
Hvis du vil fjerne gummibånd-effekten fra menyen – enten av estetiske årsaker eller fordi det, som i mitt tilfelle, førte til ekstra gjengivelsesproblemer – fjern ganske enkelt CSS-regelen -webkit-overflow-scrolling.
Men som du ser nedenfor, mister du også det jevne rullemomentet.
Hele sidens kildekode
Her er hele kildekoden til siden.
<!-- 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>