ScrollTop animé ne fonctionne pas dans firefox

Cette fonction fonctionne très bien. Il déforme le corps jusqu'à l'offset du contenant désiré

function scrolear(destino){
    var stop = $(destino).offset().top;
    var delay = 1000;
    $('body').animate({scrollTop: stop}, delay);
    return false;
}

mais pas dans Firefox. Pourquoi?

- EDIT -

pour gérer de double trigger dans la réponse acceptée, je suggère d'empiler l'élément avant l'animation:

$('body,html').stop(true,true).animate({scrollTop: stop}, delay);
163
demandé sur Toni Michel Caubet 2011-11-16 13:13:32

10 réponses

Firefox place le débordement au niveau html , sauf s'il est spécifiquement conçu pour se comporter différemment.

pour le faire fonctionner dans Firefox, utilisez

$('body,html').animate( ... );

exemple pratique

la solution CSS serait de définir les styles suivants:

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

je suppose que la solution JS serait la moins invasive.


mise à Jour

une bonne partie de la discussion ci-dessous met l'accent sur le fait que l'animation du scrollTop de deux éléments causerait le rappel d'être invoqué deux fois. Les fonctions de détection par navigateur ont été suggérées et, par la suite, dépréciées, et certaines sont sans doute assez farfelues.

Si le rappel est idempotent et ne nécessite pas beaucoup de puissance de calcul, le tirant deux fois peut être un non-problème. Si plusieurs les invocations du callback sont vraiment un problème, et si vous voulez éviter la détection de fonctionnalités, il pourrait être plus simple de faire valoir que le callback n'est lancé qu'une seule fois à partir de l'intérieur du callback:

function runOnce(fn) { 
    var count = 0; 
    return function() { 
        if(++count == 1)
            fn.apply(this, arguments);
    };
};

$('body, html').animate({ scrollTop: stop }, delay, runOnce(function() {
   console.log('scroll complete');
}));
329
répondu David Hedlund 2017-06-08 08:34:03

détection des caractéristiques et animation sur un seul objet supporté serait bien, mais il n'y a pas de solution en une seule ligne. En attendant, voici une façon d'utiliser une promesse de faire un simple rappel à chaque exécution.

$('html, body')
    .animate({ scrollTop: 100 })
    .promise()
    .then(function(){
        // callback code here
    })
});

mise à jour: Voici comment vous pouvez utiliser la détection de fonctionnalités à la place. Ce morceau de code doit être évalué avant votre appel d'animation:

// Note that the DOM needs to be loaded first, 
// or else document.body will be undefined
function getScrollTopElement() {

    // if missing doctype (quirks mode) then will always use 'body'
    if ( document.compatMode !== 'CSS1Compat' ) return 'body';

    // if there's a doctype (and your page should)
    // most browsers will support the scrollTop property on EITHER html OR body
    // we'll have to do a quick test to detect which one...

    var html = document.documentElement;
    var body = document.body;

    // get our starting position. 
    // pageYOffset works for all browsers except IE8 and below
    var startingY = window.pageYOffset || body.scrollTop || html.scrollTop;

    // scroll the window down by 1px (scrollTo works in all browsers)
    var newY = startingY + 1;
    window.scrollTo(0, newY);

    // And check which property changed
    // FF and IE use only html. Safari uses only body.
    // Chrome has values for both, but says 
    // body.scrollTop is deprecated when in Strict mode.,
    // so let's check for html first.
    var element = ( html.scrollTop === newY ) ? 'html' : 'body';

    // now reset back to the starting position
    window.scrollTo(0, startingY);

    return element;
}

// store the element selector name in a global var -
// we'll use this as the selector for our page scrolling animation.
scrollTopElement = getScrollTopElement();

utilisez maintenant le var que nous venons de définir comme le sélecteur pour la page animation défilante, et utilisez la syntaxe régulière:

$(scrollTopElement).animate({ scrollTop: 100 }, 500, function() {
    // normal callback
});
19
répondu Stephen 2014-02-05 21:46:49

j'ai passé des années à essayer de comprendre pourquoi mon code ne marcherait pas -

$('body,html').animate({scrollTop: 50}, 500);

le problème était dans mon css -

body { height: 100%};

Je l'ai mis sur auto à la place (et je me suis inquiété de savoir pourquoi il a été mis sur 100% en premier lieu). Qu'il fixe pour moi.

6
répondu Aidan Ewen 2014-03-02 19:53:08

vous pourriez vouloir esquiver le problème en utilisant un plugin – plus spécifiquement, mon plugin :)

sérieusement, même si le problème de base a été abordé depuis longtemps (différents navigateurs utilisent des éléments différents pour le défilement de la fenêtre), Il ya un certain nombre de questions non triviales en bas de la ligne qui peuvent vous faire trébucher:

je suis évidemment partial, mais jQuery.scrollable est en fait un bon choix pour répondre à ces questions. (En fait, je ne connais pas d'autre plugin qui gère tout.)

en outre, vous pouvez calculer la position de la cible – celui que vous faites défiler à – d'une manière à l'épreuve des balles avec la fonction getScrollTargetPosition() dans cette gist .

Tout ce qui vous laisserait avec

function scrolear ( destino ) {
    var $window = $( window ),
        targetPosition = getScrollTargetPosition ( $( destino ), $window );

    $window.scrollTo( targetPosition, { duration: 1000 } );

    return false;
}
2
répondu hashchange 2017-05-23 11:54:43

attention. J'ai eu le même problème, ni Firefox ni Explorer scrolling avec

$('body').animate({scrollTop:pos_},1500,function(){do X});

donc J'ai utilisé comme David a dit

$('body, html').animate({scrollTop:pos_},1500,function(){do X});

Grand cela a fonctionné, mais Nouveau problème, puisqu'il y a deux éléments, le corps et html, la fonction est exécutée deux fois, c'est-à-dire, do X exécute deux fois.

a essayé seulement avec 'html', et Firefox et Explorer fonctionnent, mais maintenant Chrome ne supporte pas cela.

So nécessaire corps pour Chrome, et html pour Firefox et Explorer. C'est un jQuery bug? ne sais pas.

il suffit de se méfier de votre fonction, car il va fonctionner deux fois.

0
répondu Avenida Gez 2013-05-28 10:08:42

je recommande et non en s'appuyant sur body ou html comme solution plus portable. Il suffit d'ajouter une div dans le corps qui vise à contenir les éléments déroulés et le style comme pour permettre le défilement taille réelle:

#my-scroll {
  position: absolute;
  width: 100%;
  height: 100%;
  overflow: auto;
}

(en supposant que display:block; et top:0;left:0; sont des valeurs par défaut qui correspondent à votre objectif), puis utilisez $('#my-scroll') pour vos animations.

0
répondu Javarome 2015-02-18 23:01:23

C'est un vrai marché. Il fonctionne sur Chrome et Firefox parfaitement. C'est même triste que des ignorants me rejettent. Ce code littéralement fonctionne parfaitement sur tous les navigateurs. Vous n'avez qu'à ajouter un lien et mettre l'id de l'élément que vous voulez faire défiler dans le href et cela fonctionne sans rien spécifier. Pur code réutilisable et fiable.

$(document).ready(function() {
  function filterPath(string) {
    return string
    .replace(/^\//,'')
    .replace(/(index|default).[a-zA-Z]{3,4}$/,'')
    .replace(/\/$/,'');
  }
  var locationPath = filterPath(location.pathname);
  var scrollElem = scrollableElement('html', 'body');

  $('a[href*=#]').each(function() {
    var thisPath = filterPath(this.pathname) || locationPath;
    if (locationPath == thisPath
    && (location.hostname == this.hostname || !this.hostname)
    && this.hash.replace(/#/,'') ) {
      var $target = $(this.hash), target = this.hash;
      if (target) {
        var targetOffset = $target.offset().top;
        $(this).click(function(event) {
          event.preventDefault();
          $(scrollElem).animate({scrollTop: targetOffset}, 400, function() {
            location.hash = target;
          });
        });
      }
    }
  });

  // use the first element that is "scrollable"
  function scrollableElement(els) {
    for (var i = 0, argLength = arguments.length; i <argLength; i++) {
      var el = arguments[i],
          $scrollElement = $(el);
      if ($scrollElement.scrollTop()> 0) {
        return el;
      } else {
        $scrollElement.scrollTop(1);
        var isScrollable = $scrollElement.scrollTop()> 0;
        $scrollElement.scrollTop(0);
        if (isScrollable) {
          return el;
        }
      }
    }
    return [];
  }
});
0
répondu drjorgepolanco 2015-08-19 16:23:43

pour moi le problème était que firefox a automatiquement sauté à l'ancre avec le nom-attribut le même que le nom de hachage que j'ai mis dans L'URL. Même si je l'ai mis .preventDefault() pour l'en empêcher. Ainsi, après avoir changé les attributs de nom, firefox n'a pas automatiquement sauter aux ancres, mais effectuer l'animation droite.

@Toni désolé si cela n'était pas compréhensible. Le truc c'est que j'ai changé les hashs dans L'URL comme www.someurl.com/#hashname. Puis j'ai eu pour exemple une ancre comme <a name="hashname" ...></a> à laquelle jQuery devrait faire défiler automatiquement. Mais il ne l'a pas fait parce qu'il a sauté directement à l'ancre avec l'attribut de nom correspondant dans Firefox sans aucune animation par rouleau. Une fois que j'ai changé l'attribut name en quelque chose de différent du nom de hachage, par exemple en name="hashname-anchor" , le défilement a fonctionné.

-3
répondu Peter 2014-12-16 10:50:37

pour moi, c'était éviter d'ajouter L'ID au point d'animation:

éviter:

 scrollTop: $('#' + id).offset().top

préparer la carte d'identité à l'avance et faire ceci à la place:

 scrollTop: $(id).offset().top

Fixe dans FF. (Le css ajouts de ne pas faire une différence pour moi)

-5
répondu bcm 2013-05-15 06:56:05
setTimeout(function(){                               
                   $('html,body').animate({ scrollTop: top }, 400);
                 },0);

espérons que ça marche.

-8
répondu Simbu 2013-05-14 13:21:00