111 votes

iOS Safari - Comment désactiver l'overscroll mais permettre aux divs défilants de défiler normalement ?

Je travaille sur une application web basée sur l'iPad, et j'ai besoin d'empêcher le défilement excessif pour que cela ressemble moins à une page web. J'utilise actuellement cette fonction pour figer la fenêtre et désactiver le défilement :

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

Cela fonctionne très bien pour désactiver le défilement, mais mon application comporte plusieurs divs défilables. le code ci-dessus les empêche de défiler .

Je ne vise que l'iOS 5 et les versions supérieures et j'ai donc évité les solutions de fortune comme iScroll. À la place, j'utilise ce CSS pour mes divs défilants :

.scrollable {
    -webkit-overflow-scrolling: touch;
    overflow-y:auto;
}

Cela fonctionne sans le document overscroll script, mais ne résout pas le problème du défilement des div.

Sans plugin jQuery, Existe-t-il un moyen d'utiliser le correctif pour l'overscroll mais sans mes divs $('.scrollable') ?

EDIT :

J'ai trouvé quelque chose qui est une solution décente :

 // Disable overscroll / viewport moving on everything but scrollable divs
 $('body').on('touchmove', function (e) {
         if (!$('.scrollable').has($(e.target)).length) e.preventDefault();
 });

La fenêtre d'affichage se déplace toujours lorsque vous faites défiler le début ou la fin de la division. J'aimerais trouver un moyen de désactiver cela également.

0 votes

J'ai essayé votre dernière version mais ça n'a pas marché non plus.

0 votes

J'ai réussi à empêcher le déplacement de la fenêtre d'affichage lorsque vous faites défiler la page au-delà de la fin de la division en capturant explicitement l'événement de défilement sur le parent de la division défilable et en ne lui permettant pas de défiler réellement. Si vous utilisez jquery mobile, il est judicieux de le faire au niveau de la page, comme suit : $('div[data-role="page"]').on('scroll', function(e) {e.preventDefault() ; }) ;

0 votes

github.com/lazd/iNoBounce fait des merveilles

84voto

Tyler Dodge Points 539

Cela résout le problème lorsque vous faites défiler le début ou la fin de la div.

var selScrollable = '.scrollable';
// Uses document because document will be topmost level in bubbling
$(document).on('touchmove',function(e){
  e.preventDefault();
});
// Uses body because jQuery on events are called off of the element they are
// added to, so bubbling would not work if we used document instead.
$('body').on('touchstart', selScrollable, function(e) {
  if (e.currentTarget.scrollTop === 0) {
    e.currentTarget.scrollTop = 1;
  } else if (e.currentTarget.scrollHeight === e.currentTarget.scrollTop + e.currentTarget.offsetHeight) {
    e.currentTarget.scrollTop -= 1;
  }
});
// Stops preventDefault from being called on document if it sees a scrollable div
$('body').on('touchmove', selScrollable, function(e) {
  e.stopPropagation();
});

Notez que cela ne fonctionnera pas si vous voulez bloquer le défilement de la page entière lorsqu'une div n'a pas de débordement. Pour bloquer cela, utilisez le gestionnaire d'événement suivant au lieu de celui qui se trouve juste au-dessus (adapté de cette question ) :

$('body').on('touchmove', selScrollable, function(e) {
    // Only block default if internal div contents are large enough to scroll
    // Warning: scrollHeight support is not universal. (https://stackoverflow.com/a/15033226/40352)
    if($(this)[0].scrollHeight > $(this).innerHeight()) {
        e.stopPropagation();
    }
});

0 votes

Cela ne fonctionnera pas s'il y a une iframe à l'intérieur de la zone de défilement et que l'utilisateur commence à faire défiler cette iframe. Existe-t-il un moyen de contourner ce problème ?

2 votes

Cela a bien fonctionné - c'est vraiment mieux que de simplement cibler. .scrollable directement (ce qui est ce que j'avais initialement essayé pour résoudre ce problème). Si vous êtes un noob JavaScript et que vous voulez un code facile pour supprimer ces gestionnaires quelque part sur la ligne, ces deux lignes fonctionnent très bien pour moi ! $(document).off('touchmove'); ET $('body').off('touchmove touchstart', '.scrollable');

0 votes

Cela a parfaitement fonctionné pour moi. Merci beaucoup, vous m'avez fait gagner des heures !

24voto

Kuba Holuj Points 171

En utilisant l'excellente méthode de Tyler Dodge réponse continuait à traîner sur mon iPad, alors j'ai ajouté un code d'accélération, maintenant c'est assez fluide. Il y a parfois quelques sauts minimes lors du défilement.

// Uses document because document will be topmost level in bubbling
$(document).on('touchmove',function(e){
  e.preventDefault();
});

var scrolling = false;

// Uses body because jquery on events are called off of the element they are
// added to, so bubbling would not work if we used document instead.
$('body').on('touchstart','.scrollable',function(e) {

    // Only execute the below code once at a time
    if (!scrolling) {
        scrolling = true;   
        if (e.currentTarget.scrollTop === 0) {
          e.currentTarget.scrollTop = 1;
        } else if (e.currentTarget.scrollHeight === e.currentTarget.scrollTop + e.currentTarget.offsetHeight) {
          e.currentTarget.scrollTop -= 1;
        }
        scrolling = false;
    }
});

// Prevents preventDefault from being called on document if it sees a scrollable div
$('body').on('touchmove','.scrollable',function(e) {
  e.stopPropagation();
});

En outre, l'ajout de la feuille de style en cascade suivante corrige certains problèmes de rendu ( source ) :

.scrollable {
    overflow: auto;
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
}
.scrollable * {
    -webkit-transform: translate3d(0,0,0);
}

0 votes

Cela ne fonctionnera pas s'il y a une iframe à l'intérieur de la zone de défilement et que l'utilisateur commence à faire défiler cette iframe. Existe-t-il un moyen de contourner ce problème ?

1 votes

Cela semble fonctionner parfaitement pour le glissement vers l'arrière, mais le glissement vers le bas déplace toujours Safari.

1 votes

Une solution géniale... Merci beaucoup :)

13voto

Jonathan Tonge Points 860

Tout d'abord, empêchez les actions par défaut sur l'ensemble de votre document comme d'habitude :

$(document).bind('touchmove', function(e){
  e.preventDefault();           
});

Ensuite, empêchez votre classe d'éléments de se propager au niveau du document. Cela l'empêche d'atteindre la fonction ci-dessus et ainsi e.preventDefault() n'est pas lancé :

$('.scrollable').bind('touchmove', function(e){
  e.stopPropagation();
});

Ce système semble plus naturel et moins intensif que le calcul de la classe sur tous les mouvements de touche. Utilisez .on() plutôt que .bind() pour les éléments générés dynamiquement.

Tenez également compte de ces métabalises pour éviter que des choses malheureuses ne se produisent lors de l'utilisation de votre division déroulante :

<meta content='True' name='HandheldFriendly' />
<meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0' name='viewport' />
<meta name="viewport" content="width=device-width" />

7voto

DeveloperJoe Points 54

Pouvez-vous ajouter un peu plus de logique dans votre code de désactivation de l'overscroll pour vous assurer que l'élément ciblé en question n'est pas un élément que vous souhaitez faire défiler ? Quelque chose comme ceci :

document.body.addEventListener('touchmove',function(e){
     if(!$(e.target).hasClass("scrollable")) {
       e.preventDefault();
     }
 });

3 votes

Merci... Il semble que devrait travailler, mais ce n'est pas le cas. De plus, ne devrait-on pas dire "scrollable" et non ".scrollable" (avec le point) ?

1 votes

Il semble que ce soit l'élément le plus profondément imbriqué qui reçoive l'événement tactile. Vous devez donc vérifier tous vos parents pour voir si vous vous trouvez dans une division déroulante.

3 votes

Pourquoi utiliser document.body.addEventListener si l'on utilise jQuery ? Y a-t-il une raison à cela ?

5voto

Johan Points 31

Vérifie si l'élément défilable est déjà défilé vers le haut lorsqu'on essaie de le faire défiler vers le haut ou vers le bas lorsqu'on essaie de le faire défiler vers le bas, puis empêche l'action par défaut d'arrêter le déplacement de la page entière.

var touchStartEvent;
$('.scrollable').on({
    touchstart: function(e) {
        touchStartEvent = e;
    },
    touchmove: function(e) {
        if ((e.originalEvent.pageY > touchStartEvent.originalEvent.pageY && this.scrollTop == 0) ||
            (e.originalEvent.pageY < touchStartEvent.originalEvent.pageY && this.scrollTop + this.offsetHeight >= this.scrollHeight))
            e.preventDefault();
    }
});

0 votes

J'ai dû vérifier pour e.originalEvent.touches[0].pageY au lieu de e.originalEvent.pageY. Cela a fonctionné mais seulement si vous êtes déjà à la fin de la div de défilement. Lorsque le défilement est en cours (par exemple, si vous avez défilé très rapidement), il ne s'arrête pas une fois que la fin de la division défilante est atteinte.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X