Contactez-nous

1

Scroll auto sur iPhone, iPod et iPad

6 avril 2010
par ARNO*

Avertissement : cette astuce n’est pas réservée à SPIP. Elle concerne n’importe quelle page HTML destinée à l’affichage sur iPhone ou iPad. (Mais personnellement, je développe jamais que du HTML avec SPIP...)

Sur Safari versions iPhone, iPad et iPad, il est très difficile d’utiliser le scroll automatique pour des éléments de hauteur fixée dotée du style :

  1. overflow: auto;

Le comportement existe, mais est généralement inconnu des usagers : le scroll s’obtient en caressant l’engin avec deux doigts. Le scroll avec un doigt est réservé au scroll général de la page.

Si votre page est conçue comme une « application Web », ce comportement est très pénible. Je n’ai pas trouvé de solution satisfaisante pour rétablir le comportement « habituel » sur ces machines, aussi j’ai développé un petit javascript (utilisant jQuery).

Je vous le livre donc ci-dessous, tel quel.

Outre le retour du scroll à un doigt sur les éléments à overflow, le script simule l’apparition et la disparition d’une scrollbar à la façon de Safari.

Pour l’utiliser, il faut ajouter la classe scroll_auto aux éléments concernés (les éléments qui sont conçus pour la classe overflow). Par exemple :

  1. <div id="cadredemo" class="scroll_auto">
  2. Ceci est mon texte.
  3. [...]
  4. Ce contenu est suffisamment long pour déclencher le système de scroll.
  5. </div>

Télécharger

Pour être cohérent, il faut évidemment que le cadre doté du scroll_auto soit de hauteur fixe et doté d’un overflow, par exemple :

  1. #cadredemo {
  2. height: 400px;
  3. overflow: auto;
  4. }

Télécharger

Il suffit ensuite d’appeler le fichier Javascript dans votre header, il déclenchera tout seul tout le comportement nécessaire.

Notez bien : si vous testez votre page Safari sous Mac (ou sous Windows) en modifiant son indication d’agent utilisateur, le comportement ne fonctionnera pas : les événements touchstart, touchmove et touchend ne sont intégrés qu’aux versions iPhone, iPod et iPad.

  1. var debut_scroll_Y = false;
  2. var debut_marginTop = 0;
  3. var hauteur_scrollbar_auto = 100;
  4.  
  5. function fix_scroll_auto() {
  6. if(
  7. (navigator.userAgent.match(/iPhone|iPod|iPad/i))
  8. ) {
  9. $(".scroll_auto").each(function() {
  10. var contenu = $(this).html();
  11. var start_time = 0;
  12. var last_decal = 0;
  13. var rapport = 1;
  14. var hauteur_max = 100;
  15. var hauteur_boite = 100;
  16.  
  17. if ($(this).children(".wrapper").width() > 0) {
  18. } else {
  19. $(this)
  20. .html( "<div class='scrollbar_auto' style='position: absolute; top: 0px; right: 3px; -webkit-box-shadow: 0px 0px 2px rgba(255,255,255, 0.3); width: 5px; height: 100px; -webkit-border-radius: 3px; background-color: rgba(0,0,0,0.5); z-index: 100; display: none; '></div>"
  21. + "<div class='wrapper' style='height: auto; -webkit-transition-property: margin-top; -webkit-transition-duration: 0.2s; -webkit-transition-timing-function: ease-out;'>"
  22. + contenu + "<div style='clear: both;'></div></div>"
  23. + "<div class='bidon' style='position: absolute; opacity: 0;'></div>"
  24. )
  25. .css("position", "relative")
  26. .css("overflow", "hidden");
  27.  
  28. }
  29.  
  30. this.ontouchstart = function(evt) {
  31. debut_scroll_Y = evt.touches[0].pageY;
  32.  
  33. var now = new Date().getTime();
  34. start_time = parseInt(now, 10);
  35. last_decal = 0;
  36.  
  37. debut_marginTop = parseInt($(this).children(".wrapper").css("marginTop"));
  38.  
  39. rapport = $(this).outerHeight() / $(this).children(".wrapper").outerHeight();
  40.  
  41. if (rapport < 1) {
  42. $(this).children(".scrollbar_auto").fadeIn();
  43. hauteur_scrollbar_auto = Math.floor(rapport * $(this).outerHeight());
  44. $(this).children(".scrollbar_auto").height(hauteur_scrollbar_auto);
  45. }
  46. hauteur_max = $(this).children(".wrapper").outerHeight() - $(this).outerHeight();
  47. hauteur_max = -1 * hauteur_max;
  48. hauteur_boite = $(this).outerHeight();
  49.  
  50. };
  51.  
  52. this.ontouchmove = function(event) {
  53. if (rapport > 1) return false;
  54.  
  55. var y = event.touches[0].pageY;
  56.  
  57. var decal = y - debut_scroll_Y;
  58. last_decal = decal;
  59. decal = decal + debut_marginTop;
  60.  
  61.  
  62. if (decal > 0) decal = 0;
  63. if (decal < hauteur_max) decal = hauteur_max;
  64.  
  65. $(this).children(".wrapper").css("marginTop", decal);
  66. // Hack bizarre: dans certains cas, il faut ecrire quelque chose quelquepart pour que le scroll fonctionne en direct
  67. $(this).children(".bidon").html("bidon");
  68.  
  69. var rapport_pos = (decal / hauteur_max);
  70.  
  71. var vide = Math.round( rapport_pos * (hauteur_boite - hauteur_scrollbar_auto));
  72.  
  73. $(this).children(".scrollbar_auto").css("marginTop", vide);
  74.  
  75.  
  76. return false;
  77.  
  78. };
  79.  
  80. this.ontouchend = function(event) {
  81. var now_end = new Date().getTime();
  82. var end_time = parseInt(now_end, 10);
  83.  
  84. var duree = end_time - start_time;
  85.  
  86. var decal = Math.round(last_decal * ( 1 + 200/duree));
  87. decal = decal + debut_marginTop;
  88.  
  89.  
  90. if (hauteur_max < 0) {
  91.  
  92. if (decal > 0) decal = 0;
  93. if (decal < hauteur_max) decal = hauteur_max;
  94.  
  95. $(this).children(".wrapper").css("marginTop", decal);
  96. // Hack bizarre: dans certains cas, il faut ecrire quelque chose quelquepart pour que le scroll fonctionne en direct
  97. $(this).children(".bidon").html("bidon");
  98. }
  99. }
  100. });
  101. }
  102. }
  103.  
  104. $(document).ready(function() {
  105. fix_scroll_auto();
  106.  
  107. });
  108.  
  109. $(document).bind("touchend touchcancel", function(){
  110. $(".scrollbar_auto").fadeOut();
  111. });

Télécharger

Version 0.2.

  • Amélioration du rendu visuel de la scrollbar.
  • Effet de transition du scroll : à la fin du scroll, si on a effectué un mouvement vertical rapide (« swipe »), le scroll continue un peu plus loin.

Version 0.3 Les clicks (pas de scroll) à l’intérieur du scroll provoquaient un décalage après un premier scroll (à noter : dans un simple « click », il n’y a semble-t-il pas de ontouchmove du tout).

Version 0.4 Optimiser le code, augmenter la vitesse d’exécution.

  • iEmpty
    Avril 2010

    Bravo, très intéressant comme fonctionnalité... !

    J’étais tombé sur ce problème du scroll de Safari mobile à l’intérieur de mes pages lorsque j’ai développé mon site permettant de personnaliser son iPhone avec des espaces vides.

    J’en étais arrivé à la conclusion que ce n’était pas possible, et j’aurais bien aimé trouver cette astuce à l’époque ! Peut être pour mon prochain site !

  • iLaimeSpip
    Mai 2010

    Excellent développement, félicitations ! Je m’étais lancé dedans et ai découvert ceci par hasard, bien joué, merci pour le temps économisé ! Si on l’utilise dans SPIP (testé en 2.1), bien mentionné l’appel en fin de fichier inc-head.html sans quoi il n’est pas pris en compte. Test ok sur iPhone 3G et 3GS en OS 3.0 et 3.1.3

  • BoOz
    Mai 2010

    Ca marche impec.

    Avec ca spip peut permettre de faire des applis iPad.

    Cool.

  • BoOz
    Mai 2010

    Il peut être nécéssaire de rappeler le script en cas de chargement dynamique du dom.

    Voici le code jQuery que j’utilise :

    1) attacher un évènement javascript qui chargera l’auto scroll à une div si elle est remplie dynamiquement après le chargement de la page (si l’utilisateur clique dans un menu par exemple).

    $('#contenu').bind('rempli', function() {
               fix_scroll_auto();
    });

    2) Déclencher l’évènement javascript quand la div est remplie.

    function affiche_contenu(div){
       var contenu = $(div).find(".coltexte").html();
       $("#contenu").html(contenu);
       //fire event
       $('#contenu').trigger('rempli');
    }

    $("div.article").each(function(){        
           this.ontouchstart = function(e){
           affiche_contenu(this);
           }
    });

    Ainsi, j’ai bien mon auto scroll dans la div #contenu qui me sert à ajouter le contenu principal quand je clique sur un menu.

    En revanche deux observations :

    1) Le script est trop lent (cf l’appli du Monde qui fait pareil mais plus vite)

    2) Il faudrait pouvoir savoir si le script de scroll est en train de scroller, car dans ce cas, un "touch" sur le contenu ne doit pas déclencher un autre évènement. (et oui, l’utilisateur est en train de scroller avec son touch, pas de faire autre chose, c’est seulement quand le scroll a terminé que le même "touch" peut déclencher un autre évènement.

  • ARNO*
    Mai 2010

    Salut Booz,

    Sur la lenteur, j’attends toujours un iPad pour pouvoir tester en vrai. J’ai effectivement constaté de la lenteur.

    Je me demande si ça n’est pas dû à la présence du -webkit-transition-duration qui se charge de la transition. J’ai l’impression que c’est lui qui introduit une latence quand on est en train de scroller avec le doigt. J’ai le même souci dans les swipe horizontaux sur Flip-Zone/iPad.

  • Julien
    Juin 2010

    Merci pour le code, je vais tenter sur mon iPad pour voir cette histoire de fluidité. Je ne sais pas si c’est le -webkit-transition-duration mais ce que je sais c’est que -webkit-box-shadow peut très vite devenir un FPS killer. Il faut s’en méfier comme de la peste, quand c’est possible lui préferer des images, avec border-image par exemple.

  • houtsi
    Juillet 2010

    j’ai essaye la iscroll javascropt de cet auteur http://cubiq.org/dropbox/iphonescroll.html

    sur mon site http://www.PROinvention.com/

    ça marche pas, je vais essayer le votre, merci à vous

  • houtsi
    Juillet 2010

    on peut pas utiliser un height en % ?

  • bozo du css
    Novembre 2010

    Merci pour le code ! Est-ce que quelqu’un a essayé de faire en sorte que le scroll soit horizontal ? J’ai essayé de modifier le code pour cela, mais j’ai du faire quelque chose de travers, cela ne fonctionne pas ...

    Aussi, comment faire pour que la scrollbar reste tout le temps visible, et sous la div plutôt qu’en sur-impression ?

    Merci pour toute aide ! :)

  • neil
    Novembre 2010

    Bonjour, je cherche une solution pour mon site ou il y a des iFrames mais scroll horizantal. Est-ce que ce JS marchera aussi avec un scrool a l’horizontal ?

  • ARNO*
    Novembre 2010

    @nell : Non, pas de scroll horizontal avec ce script, et par ailleurs ça n’est pas destiné à fonctionner sur des iframes.

  • rad_one
    Décembre 2010

    Le script marche plutôt bien malgré sa relative lenteur dommage que votre code soit si peu documenté. Quelqu’un pourrait l’améliorer.

    Merci

  • buburoi
    Décembre 2010

    J’y travaille pour en faire un pluggins jQuery qui fonctionnera pour un scroll vertical ou horizontal. J’essaye de mettre le code en ligne demain soir.

  • plog
    Janvier 2011

    Génial ! Merci

  • ion
    Juin 2011

    Ce script fonctionne très bien mis à part le fait qu’on ne peut plus zoomer sur les pages concernées. Quelqu’un a une solution ? merci.

  • Rodolphe
    Novembre 2011

    Excellent script !!! Existe t-il une version gérant également le scroll horizontal ?

    C’est tout ce qui lui manque

  • Zipopete
    Juin 2012

    Excellent ! Le script fonctionne a la perfection =)

  • alessio
    Septembre 2012

    Super, ça fonctionne ! mais je recontre des problèmes lorsque j’ai des paddings dans mes div, il ne scroll pas jusque tout en bas... une solution ?

Qui êtes-vous ?
Votre message

Ce formulaire accepte les raccourcis SPIP [->url] {{gras}} {italique} <quote> <code> et le code HTML <q> <del> <ins>. Pour créer des paragraphes, laissez simplement des lignes vides.