web-dev-qa-db-fra.com

Empêcher le rebond iOS sans désactiver la fonction de défilement

J'essaie d'implémenter une solution pour empêcher l'effet de rebond iOS dans Safari pour iOS lorsqu'un contenu de page Web est plus volumineux que la fenêtre d'affichage.

La page sur laquelle je travaille est assez spécifique dans sa structure et ressemble beaucoup à cette page http://new.salt.ch/

  • La structure de base est basée sur bootstrap.
  • Il a une barre de navigation fixe au sommet.
  • Il comporte un diaporama en arrière-plan en plein écran.
  • Le diaporama comporte une superposition fixée au bas de la fenêtre.
  • Un élément de bas de page est chargé hors de la zone de travail et n'est visible que lorsque vous faites défiler le contenu.
  • Le contenu défile derrière la barre de navigation.
  • Le contenu consiste en un titre placé 20px au-dessous de la barre de navigation et une série de boutons positionnés 20px au-dessus de la fenêtre.
  • Lors du défilement, les boutons et le titre se déplacent tous vers le haut de l'écran pour afficher le pied de page.

Le problème que je rencontre est identique à celui de la page http://new.salt.ch/ . En effet, lorsque vous faites défiler l'écran vers le haut, vous obtenez un effet de rebond en bas de l'écran qui révèle les fond et superposition.

J'ai essayé diverses solutions, notamment iNoBounce.js, Nonbounce.js et quelques autres suggestions que j'ai trouvées sur SO.

J'ai toujours le même problème ... lorsque j'essaie de désactiver le rebond, tout le défilement est désactivé. J'imagine que c'est parce que le contenu (autre que le pied de page) est toujours suffisamment volumineux pour que le défilement ne soit pas nécessaire. Le défilement est donc désactivé et le pied de page n'est plus accessible sur le défilement.

16
Ali Samii

Ce code devrait arrêter le rebond car c'est la balise HTML qui rebondit

html {
    height  : 100%;
    overflow: hidden;
}
body {
    height  : 100%;
    overflow: auto;
}
16
James Campbell

Si j'interprète correctement votre question, nous avons le même problème depuis des années en développant des applications Web mobiles multiplates-formes, en essayant de faire en sorte que toutes les fonctionnalités de défilement propriétaires fonctionnent correctement sur chaque appareil: Apple iOS, Google Android, Windows. Téléphone, ordinateur portable Chrome, ordinateur portable Safari, IE et ordinateur portable Edge.

jQuery Mobile continue d'essayer de résoudre ce problème dans les limites de son cadre, mais c'est trop compliqué, avec les mises à jour constantes de chaque fabricant de périphérique/fabricant de système d'exploitation.

Oui, nous avons des solutions pour chaque appareil mobile individuel. Et nous avons testé, mais n'avons pas sérieusement envisagé de développer des structures de pagination sélectives pour chaque périphérique, nous obligeant à détecter chaque périphérique et à présenter une structure légèrement différente pour chacun. Insanement mauvaise idée de maintenir au moins 3 et même jusqu'à une douzaine de versions différentes de votre code.

SOLUTION: Nous avons eu le plus de chance en mettant simplement votre en-tête et votre pied de page persistants au-dessus de la structure de votre page. Voici la solution générale utilisant des styles en ligne pour plus de simplicité:

<html>
<head>
  <title>Fixed Header and Footer on All Mobile Web Apps</title>
  <meta name="viewport" content="user-scalable=no, initial-scale=1.0, maximum-scale=1.0" />
  <style>
    html, body { height:100%; width:100%; }
  </style>
</head>
<body>
<div style="position: fixed; height:100%; width:100%; top:0; left:0;">
  <div style="height:calc(100% - 1px); width:100%; margin-top: 60px; z-index: 1; overflow-y: scroll; -webkit-overflow-scrolling: touch;">
    [Body Content That Can Scroll if it extends beyond screen...]

  </div>
  <div style="position: absolute; top:0; left:0; width:100%; height: 60px; background:#dddddd; z-index:10;">
    [Header Content...]

  </div>
  <div style="position: absolute; bottom:0; left:0; width:100%; height: 40px; background:#cccccc; z-index:11;">
    [Footer Content...]

  </div>
</div>
</body>
</html>

Ainsi, Body pourrait être n’importe quel ensemble de pages de jQuery Mobile. En fait, théoriquement, le corps pourrait être n'importe quel contenu de n'importe quel cadre.

Note spéciale, la ligne avec la hauteur: calc (100% - 1px); est essentiel à la magie.

Les combinaisons ou les permutations apparemment infinies de cette question sont presque devenues une croisade pour nous au fil des ans, en essayant de trouver la solution la plus pure, la plus simple et la plus universellement compatible. Donc, après avoir consacré un nombre embarrassant d'heures-personnes à ce sujet, il ne s'agit pas seulement de notre meilleure solution, mais également de la SEULE approche universellement compatible que nous avons trouvée, qui vous permet également de vous en tenir à une base de code unique. Il a été testé avec succès sur les dernières versions d'iOS, Windows Phone, Android, Chrome, Safari, PhoneGap, Firefox, IE 9-11 et Windows Edge.

TAGS: application mobile, application Web, en-tête fixe, pied de page fixe, en-tête persistant, pied de page persistant, problème de défilement, rebond de défilement iOS, rebond de défilement Chrome, rebond de défilement Android, problème de défilement Webkit, défilement Webkit Touch

16
Jeremy Whitt

J'ai parcouru quelques réponses sur SO et la situation semblait sombre jusqu'à ce que le code soit tombé par hasard.

html {
  position: fixed;
  height: 100%;
  overflow: hidden;
}

body {
  width: 100vw;
  height: 100vh;
  overflow-y: scroll;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
}

Les déclarations de style pour body peuvent être placées sur tout élément que vous souhaitez pouvoir faire défiler. Vous pouvez également modifier overflow-x et overflow-y si nécessaire. Personnellement, j’en avais besoin pour ne PAS faire défiler les côtés, je l’ai donc déclaré.

mise à jour du 15 sept. 2017: je devais utiliser ce correctif pour un autre projet et je pouvais me passer de ces déclarations position: fixed et height: 100%; sur le sélecteur html. YMMV

Mise à jour du 12 avril 2018 (mentionné dans les commentaires): Si vous utilisez des éléments fixes sur la page, ceux-ci peuvent présenter un "tremblement" visuel lors du défilement.

12
Sgnl

J'ai utilisé iNoBounce https://github.com/lazd/iNoBounce et cela fonctionne parfaitement!

Si vous devez également autoriser le défilement horizontal, il existe une demande d'extraction de santi6291 at https://github.com/lazd/iNoBounce/pull/36 qui corrige ce problème.

4
Zhong Huiwen

J'ai réussi à résoudre la plupart des problèmes liés à overflow: auto et overflow: scroll sur Safari mobile:

  • sans suspendre la vue de défilement après avoir touché le début de la liste, puis en descendant puis en remontant (Safari mobile exécute alors son comportement de renvoi par défaut pour toute la page)
  • prise en charge de la barre d'en-tête/d'action fixe en haut sans chevauchement déplorable avec une barre de défilement

window.addEventListener('DOMContentLoaded', function () {
                        
  var atTop = true;
  var atBottom = false;

  var body = document.getElementById('fixedBody');
  body.addEventListener('touchmove', function(event){
    event.preventDefault();
  });

  body.addEventListener('touchstart', function(event){
    event.preventDefault();
  });

  body.addEventListener('touchend', function(event){
    event.preventDefault();
  });

  var scrollingDiv = document.getElementById('scrollDiv');
  if (scrollingDiv.scrollHeight <= scrollingDiv.clientHeight) {
    atBottom = true;
  }

  scrollingDiv.addEventListener('scroll', function(event){
    
    if (event.target.scrollTop === 0) {
      atTop = true;
    } else {
      atTop = false;
    }
    
    if (event.target.scrollHeight - event.target.scrollTop === event.target.clientHeight) {
      atBottom = true;
    } else {
      atBottom = false;
    }
  });
  
  var lastY = 0;
  var topLock = false;
  var bottomLock = false;
  
  scrollingDiv.addEventListener('touchmove', function(event){
    event.stopPropagation();
    var currentY = event.touches[0].clientY;
    if (currentY > lastY) {
      // moved down
      if (atTop) {
        event.preventDefault();
        topLock = true;
      }

      if (bottomLock) {
        bottomLock = false;
        // TODO: Emulate finger remove and touch again here
      }
    } else if(currentY < lastY){
      // moved top
      if (atBottom) {
        event.preventDefault();
        bottomLock = true;
      }

      if (topLock) {
        topLock = false;
        // TODO: Emulate finger remove and touch again here
      }
    }
     
    lastY = currentY;
  });

  scrollingDiv.addEventListener('touchstart', function(event){
    lastY = event.touches[0].clientY;
    event.stopPropagation();
  });

  scrollingDiv.addEventListener('touchend', function(event){
    event.stopPropagation();
  });

});
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/>
</head>
<body id="fixedBody" style="overflow: hidden;">
  <div style="position: fixed; height: 64px; width:100%; top:0; left:0; background-color: green; z-index: 1;">
    Header
  </div>
  <div id="scrollDiv" style="position: absolute; width: 100%; top: 64px; bottom: 0px; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; background-color: white;">
    <div style="height: 150px; background-color: blue;">
      First
    </div>
    <div style="height: 150px; background-color: red;">
      Second
    </div>
    <div style="height: 150px; background-color: green;">
      Third
    </div>
    <div style="height: 150px; background-color: black;">
      Another
    </div>
  </div>
</body>
</html>

La seule mise en garde que j'ai est que lorsque l'utilisateur touche et commence à descendre, puis à monter, rien ne se passe (attendu: la liste doit défiler vers le bas). Mais au moins, la méthode empêche le "pseudo-défilement" et ne confond pas l’utilisateur.

Pour surmonter ce dernier problème, il est nécessaire d'émuler une fin tactile, puis de toucher l'événement de début lorsque la direction change (j'ai mis des commentaires "TODO").

Mise à jour: il est possible d'éviter d'utiliser un correctif de code JavaScript sur iOS Cordova avec DisallowOverscroll = true et WKWebView.

0
Brian Haak