Tenga en cuenta que esta entrada del blog se publicó en junio de 2015, por lo que, dependiendo de cuándo la lea, algunas partes podrían estar desactualizadas. Lamentablemente, no siempre puedo mantener estas publicaciones completamente actualizadas para garantizar que la información siga siendo precisa.
- Mi página de prueba
- Comprender el comportamiento predeterminado
- Entendiendo qué hace la regla CSS "-webkit-overflow-scrolling"
- El usuario debe esperar a que termine el efecto de rebote.
- Cómo evitar que la página de fondo se desplace
- Cómo evitar que el fondo se desplace en exceso
- Cómo evitar el desplazamiento excesivo hacia la página de fondo
- Código fuente de toda la página
Para solucionar un reciente problema de desplazamiento en Safari en iOS, tuve que crear mi propia página de prueba simple y luego aplicar toda la información y las combinaciones que pude encontrar en Internet para intentar resolver el problema.
A continuación, se presenta un breve resumen de lo que descubrí durante este proceso.
Mi página de prueba
Mi página de prueba era bastante básica. Consistía en datos simulados en un div estático para representar la "página", junto con un div fijo para representar un "menú" superpuesto sobre la página, así.
El código fuente completo de la página se puede encontrar al final de esta entrada del blog.
Comprender el comportamiento predeterminado
Lo primero que hice fue tratar de entender el comportamiento por defecto de Safari en iOS. Lo más notable fue que, al desplazarte hasta el fondo del menú, el cuerpo de la página mostraba un efecto de banda elástica, una característica integrada de Safari.
Lo mismo pasó al desplazarse hasta la parte superior del menú.
Sin embargo, el primer problema que noté fue que, si te desplazas de forma continua hasta la parte superior o inferior, Safari empieza a mostrar fallos de renderizado tanto en la página de fondo como en el menú.
No estoy seguro de por qué, pero sospecho que tiene algo que ver con la función de selección por banda.
Entendiendo qué hace la regla CSS "-webkit-overflow-scrolling"
Lo siguiente que hice fue aplicar la regla CSS -webkit-overflow-scrolling al menú, lo cual permite el desplazamiento con inercia.
Como se muestra en la imagen de abajo, el desplazamiento continúa incluso después de deslizar con el dedo.
Pero también añade un efecto de banda elástica al desplazarte hasta la parte superior o inferior, como se muestra a continuación.
El usuario debe esperar a que termine el efecto de rebote.
Durante mis pruebas, también observé que, mientras el efecto de banda elástica está en curso, el usuario no puede cambiar el enfoque a otro elemento hasta que la animación haya terminado por completo.
En el ejemplo a continuación, primero desplazo la página de fondo y luego intento desplazar el menú rápidamente, pero termino desplazando la página de fondo.
Sin embargo, si espero un momento a que termine el efecto, entonces ya puedo empezar a desplazarme por el menú.
Cómo evitar que la página de fondo se desplace
Durante mis pruebas, también observé que, mientras el efecto de rebote está en curso, el usuario no puede cambiar el foco a otro elemento hasta que la animación haya terminado por completo.
A partir de esta respuesta de Stack Overflow, puedes asegurarte de que los elementos con disable-scrolling no realicen su acción de desplazamiento por defecto cuando se dispare el 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();
}
};
Y luego aplica la clase disable-scrolling al div de la página:
<div class="page disable-scrolling">
Esto funciona más o menos, pero pruebas más exhaustivas muestran que la página de fondo aún puede desplazarse a veces cuando llegas a la parte superior o inferior del menú.
Cómo evitar que el fondo se desplace en exceso
Al aplicar esta solución, es posible evitar el desbordamiento. Esto significa que cuando un elemento llega a la parte superior o inferior de su recorrido, el desplazamiento no continúa hacia el cuerpo de la página.
Como resultado, se evita el desplazamiento del cuerpo, lo que también impide que ocurra el efecto de rebote.
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"));
Lo que hace este fragmento de JavaScript es evitar que el elemento alcance la parte superior o la inferior, manteniéndolo a solo 1 píxel de distancia.
Si la posición de desplazamiento nunca llega a la parte superior ni a la inferior, el desbordamiento no puede ocurrir.
Cómo evitar el desplazamiento excesivo hacia la página de fondo
Si quieres eliminar el efecto de banda elástica del menú, ya sea por motivos estéticos o porque, como en mi caso, causó problemas de renderizado adicionales, simplemente elimina la regla CSS -webkit-overflow-scrolling.
Sin embargo, como puedes ver a continuación, también se pierde la inercia del desplazamiento suave.
Código fuente de toda la página
Aquí está el código fuente completo de la página.
<!-- 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>