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);
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( ... );
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');
}));
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
});
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.
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:
- simplement animer à la fois
body
ethtml
a ses problèmes , - -tester le comportement réel du navigateur est délicat pour obtenir le droit (voir mes commentaires sur la réponse de @Stephen's ),
- mais le plus important, il ya un ensemble de problèmes d'utilisabilité que vous souhaitez traiter pour une expérience utilisateur décent.
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;
}
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.
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.
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 [];
}
});
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é.
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)
setTimeout(function(){
$('html,body').animate({ scrollTop: top }, 400);
},0);
espérons que ça marche.