web-dev-qa-db-fra.com

Empêcher le débordement / le défilement de l'élastique sur iOS

Il y a déjà plusieurs questions sur le thème du débordement/défilement de l'élastique sur SO but

  1. aucun d'eux ne fournit une solution qui fonctionne pour tous les cas sur iOS 9.3.2
  2. aucun ne donne des informations complètes et complètes sur le problème lui-même

c'est pourquoi j'ai créé ce poste comme un ensemble de connaissances.


Le problème:

La chose qui n'a jamais été mentionnée dans aucun autre article est que le défilement de débordement iOS est en fait un comportement en deux parties.

1. Débordement du défilement du contenu avec overflow: auto/scroll

Il s'agit du comportement généralement connu et recherché d'un élément avec -webkit-overflow-scrolling: touch où le comportement de défilement continu/dynamique passe devant le conteneur d'éléments pour ralentir le contenu défilé en douceur.

Cela se produit lorsque vous faites défiler le contenu d'un élément avec un élan suffisamment élevé pour que le défilement de l'élan dépasse la longueur du contenu défilé.

Avec ce comportement, le element.scrollTop propriété changeant en conséquence à la position de défilement des éléments et étant inférieure à 0 ou supérieure au défilement maximal (element.scrollHeight - element.offsetHeight).

2. Débordement du défilement de <body>

Ce problème se produit si vous essayez de faire défiler un élément déjà à sa position de défilement minimum/maximum encore plus loin (un élément de haut en haut ou un élément de bas en bas). Ensuite, le défilement semble "bouillonner" jusqu'au <body> tag et la fenêtre entière défile.

Contrairement à ce qui précède ici, le element.scrollTop la propriété ne change pas mais document.body.scrollTop change à la place.

Verrouillage de la mise au point et commutation entre les comportements (délai de 1,5 s)

La chose la plus irritante dans ce contexte est que le basculement entre les deux types décrits ci-dessus ne change pas instantanément.

Après avoir entré l'un des deux, vous ne pouvez pas basculer le focus sur un autre élément (éléments défilants, boutons, liens, ...) et le comportement de défilement ne change donc pas aussi.

Par exemple: si vous faites défiler un élément déjà à sa position supérieure vers le haut, vous entrez overflow scrolling type 2 et la réaction la plus naturelle pour un utilisateur est d'essayer de faire défiler vers le bas. Parce que le focus est verrouillé sur le défilement du corps au lieu d'aller à overflow scrolling type 1 il reste dans type 2 et tout le corps défile vers le bas. L'utilisateur typique commence alors arbitrairement à faire défiler fréquemment vers le haut et vers le bas sans jamais sortir de type 2.

Le changement de focus et donc le changement de comportement de défilement ne peut se produire qu'après la fin de l'animation de débordement et que l'élément reste immobile (même un peu plus long [environ 0,5 s]).

revenant ainsi à l'exemple précédent, la réaction correcte de l'utilisateur serait d'arrêter de toucher l'écran pendant environ 1 à 1,5 secondes, puis d'essayer de faire défiler à nouveau vers le bas.

16
Aides

La solution:

Type 1:

La solution la plus basique pour empêcher le défilement de débordement sur l'élément lui-même est d'empêcher les événements tactiles par défaut.

document.body.addEventListener('touchmove', function(e) { 
    e.preventDefault(); 
});

Cette méthode désactive cependant le défilement de l'élan natif du navigateur et n'est donc pas adaptée à la plupart des applications. Cependant, avec un certain raffinement (à éviter uniquement en cas de défilement vers le haut ou vers le bas, ...), cette méthode résout la plupart des problèmes. De nombreuses implémentations possibles peuvent être trouvées dans this SO post .

Type 2:

Le débordement du défilement sur le corps n'est cependant pas empêché par les méthodes décrites ci-dessus.

Une solution possible qui semble raisonnable est d'empêcher que l'élément ne soit jamais à sa position supérieure ou inférieure, comme décrit comme la meilleure solution sur question mentionnée .

anElement.addEventListener('touchstart', function( event ){
    if( this.scrollTop === 0 ) {
        this.scrollTop += 1;
    } else if( this.scrollTop + this.offsetHeight >= this.scrollHeight ) {
        this.scrollTop -= 1;
    }
}

Cependant, cela n'a pas fonctionné de manière fiable sur iOS 9.3.2.

Ce qui a fonctionné cependant est de définir position: fixed sur le <body> élément pour empêcher le corps de bouger. Veuillez noter cependant que cela ne s'arrête toujours pas complètement type 2 de se produire, c'est pourquoi parfois vous ne pouvez pas faire défiler/focaliser un élément car en arrière-plan type2 avec son verrouillage de la mise au point est toujours en cours (encore une fois, après avoir arrêté de toucher l'écran pendant un moment, il fonctionne à nouveau comme prévu).

Bien que ce soit encore loin d'être la solution optimale, cela semble être le meilleur que nous puissions obtenir pour le moment.

Edit: Veuillez noter que je ne sais pas s'il est sûr de mettre position: fixed sur un <body> élément. Pour suivre les problèmes possibles que j'ai créés suivant SO post . Apparemment, il pourrait être préférable de créer un élément wrapper en tant qu'enfant du corps et de définir cet élément sur position: fixed pour éviter les problèmes de zoom.


Edit 2: La solution définitive

Le script iNoBounce fait des merveilles. Chargez-le simplement sur la page et découvrez une application Web sans rebond. Jusqu'à présent, je n'ai trouvé aucun problème avec cette solution.

13
Aides