web-dev-qa-db-fra.com

document.ontouchmove et défilement sur iOS 5

iOS 5 a apporté un certain nombre de fonctionnalités intéressantes à JavaScript/Web Apps. L'un d'eux est le défilement amélioré. Si vous ajoutez

-webkit-overflow-scroll:touch;

au style d'un élément de zone de texte, le défilement fonctionnera bien avec un seul doigt.

Mais il y a un problème. Pour empêcher tout l'écran de défiler, il est recommandé que les applications Web ajoutent cette ligne de code:

document.ontouchmove = function(e) {e.preventDefault()};

Cependant, cela désactive le nouveau défilement.

Quelqu'un a-t-il une bonne façon d'autoriser le nouveau défilement dans une zone de texte, mais pas de faire défiler le formulaire entier?

47
ghenne

Mise à jour Selon commentaire d'Alvaro , cette solution peut ne plus fonctionner à partir d'iOS 11.3.

Vous devriez pouvoir autoriser le défilement en sélectionnant si preventDefault est appelé ou non. Par exemple.,

document.ontouchmove = function(e) {
    var target = e.currentTarget;
    while(target) {
        if(checkIfElementShouldScroll(target))
            return;
        target = target.parentNode;
    }

    e.preventDefault();
};

Alternativement, cela peut fonctionner en empêchant l'événement d'atteindre le niveau du document.

elementYouWantToScroll.ontouchmove = function(e) {
    e.stopPropagation();
};

Modifier Pour toute personne lisant plus tard, la réponse alternative fonctionne et est beaucoup plus facile.

56
Brian Nickel

Le seul problème avec la réponse de Brian Nickel est que (comme l'a mentionné user1012566) stopPropagation n'empêche pas le bouillonnement lorsque vous atteignez les limites de votre défilement. Vous pouvez éviter cela avec les éléments suivants:

elem.addEventListener('touchstart', function(event){
    this.allowUp = (this.scrollTop > 0);
    this.allowDown = (this.scrollTop < this.scrollHeight - this.clientHeight);
    this.prevTop = null; 
    this.prevBot = null;
    this.lastY = event.pageY;
});

elem.addEventListener('touchmove', function(event){
    var up = (event.pageY > this.lastY), 
        down = !up;

    this.lastY = event.pageY;

    if ((up && this.allowUp) || (down && this.allowDown)) 
        event.stopPropagation();
    else 
        event.preventDefault();
});
22
1nfiniti

Pour quiconque essaie de réaliser cela avec PhoneGap, vous pouvez désactiver le défilement élastique dans le cordova.plist, définissez la valeur de UIWebViewBounce sur NO. J'espère que cela aide tous ceux qui passent des âges là-dessus (comme moi).

16
Andrew Plummer

ScrollFix semble être la solution parfaite. Je l'ai testé et ça marche comme un charme!

https://github.com/joelambert/ScrollFix

/**
 * ScrollFix v0.1
 * http://www.joelambert.co.uk
 *
 * Copyright 2011, Joe Lambert.
 * Free to use under the MIT license.
 * http://www.opensource.org/licenses/mit-license.php
 */

var ScrollFix = function(elem) {
    // Variables to track inputs
    var startY, startTopScroll;

    elem = elem || document.querySelector(elem);

    // If there is no element, then do nothing  
    if(!elem)
        return;

    // Handle the start of interactions
    elem.addEventListener('touchstart', function(event){
        startY = event.touches[0].pageY;
        startTopScroll = elem.scrollTop;

        if(startTopScroll <= 0)
            elem.scrollTop = 1;

        if(startTopScroll + elem.offsetHeight >= elem.scrollHeight)
            elem.scrollTop = elem.scrollHeight - elem.offsetHeight - 1;
    }, false);
};
6
vladaman

Il était frustrant de découvrir un problème connu avec stopPropagation et le défilement natif des div. Cela ne semble pas empêcher le onTouchMove de bouillonner, de sorte qu'en faisant défiler au-delà des limites de la div (vers le haut en haut ou vers le bas en bas), la page entière rebondisse.

Plus de discussion ici et ici .

2
user1012566