Si prega di notare che questo post del blog è stato pubblicato a giugno 2015, quindi, a seconda di quando lo si legge, alcune parti potrebbero non essere aggiornate. Purtroppo, non posso sempre mantenere questi post completamente aggiornati per garantire che le informazioni rimangano accurate.
- La mia pagina di prova
- Capire il comportamento predefinito
- Capire cosa fa la regola CSS "-webkit-overflow-scrolling"
- L'utente deve aspettare che l'effetto elastico termini
- Come impedire lo scorrimento della pagina di sfondo
- Come impedire lo scorrimento della pagina di sfondo
- Come evitare lo scorrimento eccessivo verso la pagina di sfondo
- Codice sorgente dell'intera pagina
Per risolvere un recente problema di scorrimento in Safari su iOS, sono stato costretto a creare una semplice pagina di test e, successivamente, a mettere in pratica tutte le informazioni e le combinazioni che ho trovato sul web per cercare di risolvere il problema.
Di seguito trovi una breve panoramica di ciò che ho scoperto durante questo processo.
La mia pagina di prova
La mia pagina di prova era abbastanza semplice. Era costituita da dati simulati in un div statico per rappresentare la "pagina", insieme a un div fisso per rappresentare un "menu" sovrapposto alla pagina, così.
Il codice sorgente completo della pagina si trova in fondo a questo post del blog.
Capire il comportamento predefinito
La prima cosa che ho fatto è stata cercare di capire il comportamento predefinito di Safari su iOS. L'osservazione più notevole è stata che, quando si scorreva fino in fondo al menu, il corpo della pagina mostrava l'«effetto elastico» — una funzione integrata in Safari.
È successo lo stesso anche quando si è arrivato in cima al menu.
Tuttavia, il primo problema che ho notato è che se scorri continuamente fino in cima o fino in fondo, Safari inizia a mostrare problemi di rendering sia sulla pagina di sfondo sia nel menu.
Non sono sicuro del perché, ma sospetto che abbia a che fare con la funzionalità elastica.
Capire cosa fa la regola CSS "-webkit-overflow-scrolling"
La prossima cosa che ho fatto è stata applicare la regola CSS -webkit-overflow-scrolling al menu, che consente lo scorrimento basato sull'inerzia.
Come mostrato nell'immagine qui sotto, lo scorrimento continua anche dopo aver effettuato uno swipe con il dito.
Ma aggiunge anche un effetto elastico quando si scorre fino in cima o fino in fondo, come mostrato di seguito.
L'utente deve aspettare che l'effetto elastico termini
Durante i miei test, ho notato anche che, durante l'effetto elastico in corso, l'utente non può spostare il focus su un altro elemento finché l'animazione non è completamente terminata.
Nell'esempio qui sotto, scorro prima la pagina di sfondo e poi cerco di scorrere rapidamente il menu, ma finisco per continuare a scorrere la pagina di sfondo.
Tuttavia, se aspetto un attimo che l'effetto finisca, posso poi iniziare a scorrere il menù.
Come impedire lo scorrimento della pagina di sfondo
Durante i miei test, ho anche notato che, mentre l'effetto elastico è in corso, l'utente non può spostare il focus su un altro elemento finché l'animazione non è completamente terminata.
Basata su questa risposta di Stack Overflow, puoi assicurarti che gli elementi con disable-scrolling non eseguano l'azione di scorrimento predefinita quando viene attivato l'evento 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();
}
};
E poi applica la classe disable-scrolling al div della pagina:
<div class="page disable-scrolling">
Questo funziona abbastanza bene, ma test più approfonditi hanno mostrato che la pagina di sfondo può comunque scorrere a volte quando si raggiunge la parte superiore o inferiore del menu.
Come impedire lo scorrimento della pagina di sfondo
Applicando questa soluzione, è possibile prevenire "overscrolling". Ciò significa che, quando un elemento arriva al punto più alto o al punto più basso dello scorrimento, lo scorrimento non prosegue nel corpo della pagina.
Di conseguenza, viene impedito lo scorrimento del corpo della pagina, e ciò evita anche che si verifichi l'effetto elastico (rubber-band).
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"));
Questo frammento JavaScript impedisce all'elemento di raggiungere mai il bordo superiore o inferiore, mantenendolo a solo 1 pixel di distanza.
Se la posizione di scorrimento non raggiunge mai la parte superiore assoluta né quella inferiore, l'overscroll non può verificarsi.
Come evitare lo scorrimento eccessivo verso la pagina di sfondo
Se vuoi rimuovere l'effetto elastico dal menu, sia per motivi estetici sia perché, come nel mio caso, ha causato ulteriori problemi di rendering, rimuovi semplicemente la regola CSS -webkit-overflow-scrolling.
Tuttavia, come puoi vedere qui sotto, perdi anche la fluidità dello scorrimento.
Codice sorgente dell'intera pagina
Ecco il codice sorgente completo della pagina.
<!-- 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>