Accro professionnel à Internet • Passionné de jeux • Créateur de technologie
Accro professionnel à Internet • Passionné de jeux • Créateur de technologie

Six choses que j'ai apprises sur le défilement élastique de Safari sur iOS

Pour résoudre un problème de défilement dans Safari sur iOS, j'ai dû créer ma propre page de test simple !
Cette page a été traduite de l'anglais par mes stagiaires en IA, très motivés, pour votre commodité. Ils sont encore en phase d'apprentissage, et quelques erreurs ont donc pu s'y glisser. Pour des informations plus précises, veuillez vous référer à la version anglaise.
Maison Blog Six choses que j'ai apprises sur le défilement élastique de Safari sur iOS

Veuillez noter que cet article a été publié en juin 2015. Par conséquent, selon la date à laquelle vous le lisez, certaines parties peuvent être obsolètes. Malheureusement, je ne peux pas toujours maintenir ces articles à jour pour garantir l'exactitude des informations.

Pour résoudre un problème de défilement récent dans Safari sur iOS, j'ai dû créer ma propre page de test simple, puis appliquer toutes les informations et combinaisons que j'ai pu trouver sur Internet afin d'essayer de résoudre le problème.

Ci-dessous, un bref aperçu de ce que j'ai découvert au cours de ce processus.

Ma page de test

Ma page de test était assez simple. Elle se composait de données factices dans une div statique pour représenter la « page », ainsi qu’une div fixe représentant un « menu » superposé au-dessus de la page, comme ceci.

Une capture d'écran d'un menu latéral HTML ouvert dans Safari sur iOS

Le code source complet de la page se trouve en bas de cet article de blog.

Comprendre le comportement par défaut

La première chose que j'ai faite a été d'essayer de comprendre le comportement par défaut de Safari sur iOS. L'observation la plus notable était que lorsque vous faisiez défiler jusqu'au bas du menu, le corps de la page affichait un « effet élastique » — une fonctionnalité codée en dur dans Safari.

une capture d'écran animée montrant l'effet d'élasticité problématique dans Safari sur iOS, en bas de la page

La même chose s'est produite lorsque vous avez défilé jusqu'en haut du menu.

une capture d'écran animée de l'effet élastique problématique dans Safari sur iOS en haut de la page

Cependant, le premier problème que j’ai remarqué est que, si vous faites défiler continuellement jusqu’en haut ou jusqu’en bas, Safari commence à afficher des artefacts de rendu sur la page d’arrière-plan et sur le menu.

Je ne suis pas sûr de la raison, mais je pense que cela a quelque chose à voir avec la fonctionnalité 'rubber-band'.

une capture d'écran animée du problème de Safari sur iOS qui bugue en bas de la page

Comprendre ce que fait la règle CSS « -webkit-overflow-scrolling »

La prochaine chose que j'ai faite a été d'appliquer la règle CSS -webkit-overflow-scrolling au menu, qui permet le défilement avec élan.

Comme indiqué dans l'image ci-dessous, le défilement se poursuit même après avoir glissé le doigt.

présenter le problème de défilement par inertie sur iOS Safari

Mais cela crée aussi un effet élastique lorsque vous faites défiler jusqu'en haut ou en bas, comme indiqué ci-dessous.

mettre en évidence le problème de défilement par inertie sur iOS Safari avec l’effet de rebond

L'utilisateur doit attendre que l'effet de rebond se termine.

Lors de mes tests, j'ai également remarqué que, lorsque l'effet élastique est en cours, l'utilisateur ne peut pas déplacer le focus vers un autre élément tant que l'animation n'est pas entièrement terminée.

Dans l'exemple ci-dessous, je fais d'abord défiler la page d'arrière-plan, puis j'essaie rapidement de faire défiler le menu, mais je finis par continuer à faire défiler la page d'arrière-plan.

montrant que la page d'arrière-plan défile différemment sur iOS Safari

Cependant, si j'attends un instant que l'effet se termine, je peux ensuite commencer à faire défiler le menu.

montrer l'attente jusqu'à la fin de l'effet élastique sur iOS Safari

Comment empêcher la page d'arrière-plan de défiler

Lors de mes tests, j'ai aussi remarqué que pendant que l'effet élastique est en cours, l'utilisateur ne peut pas déplacer le focus vers un autre élément tant que l'animation n'est pas entièrement terminée.

En vous basant sur cette réponse de Stack Overflow, vous pouvez vous assurer que les éléments ayant la classe disable-scrolling ne déclenchent pas leur action de défilement par défaut lorsque l'événement touchmove est déclenché.

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(); } };

Et appliquez ensuite la classe disable-scrolling à la div de la page :

<div class="page disable-scrolling">

Ça fonctionne plutôt pas mal, mais des tests plus approfondis montrent que la page d'arrière-plan peut encore parfois défiler lorsque vous atteignez le sommet ou le bas du menu.

montrant que l'arrière-plan est encore en cours de défilement

Comment éviter le défilement excessif vers la page d'arrière-plan

En appliquant ce correctif, il devient possible d'éviter le défilement excessif. Cela signifie que lorsque le défilement d'un élément atteint son sommet ou son bas, le défilement ne se propage pas dans le corps de la page.

En conséquence, le défilement du corps est bloqué, ce qui empêche également l'effet rebond de se produire.

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"));

Ce petit extrait JavaScript empêche l’élément d’atteindre le sommet ou le bas en le maintenant à seulement 1 pixel près.

Si la position de défilement n'atteint jamais le sommet absolu ni le bas absolu, le débordement de défilement ne peut jamais se produire.

Démo montrant comment empêcher le défilement excessif en n'atteignant jamais le tout début ni la toute fin

Comment éviter le défilement excessif vers la page d'arrière-plan

Si vous souhaitez supprimer l'effet élastique du menu — que ce soit pour des raisons esthétiques ou parce que, comme dans mon cas, il a entraîné des problèmes de rendu supplémentaires — il suffit de supprimer la règle CSS -webkit-overflow-scrolling.

Cependant, comme vous pouvez le voir ci-dessous, vous perdez également l'inertie du défilement fluide.

Démo complète pour prévenir l'effet rubberbanding sur iOS Safari

Code source de la page entière

Voici le code source complet de la page.

<!-- 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>

Écrit par Special Agent Squeaky. Première publication : 10/06/2015. Dernière mise à jour : 10/06/2015.

📺 Regardez la dernière vidéo de Squeaky !

Comment ajouter des sous-titres en temps réel simples à votre diffusion en direct