Animate scrollTop在Firefox中不起作用


164

此功能工作正常。它将主体滚动到所需容器的偏移量

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

但不是在Firefox中。为什么?

-编辑-

为了处理接受的答案中的双重触发,我建议在动画之前停止元素:

$('body,html').stop(true,true).animate({scrollTop: stop}, delay);

2
您的最终代码是这里所有内容中最精美的代码。
Lizardx

Answers:


331

Firefox会将溢出置于html级别上,除非专门设置其行为方式有所不同。

要使其在Firefox中运行,请使用

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

工作实例

CSS解决方案是设置以下样式:

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

我认为JS解决方案的侵入性最低。


更新资料

下面的许多讨论都集中在以下事实上:scrollTop对两个元素进行动画处理会导致回调被调用两次。已经提出了浏览器检测功能,但随后不推荐使用这些功能,其中一些功能可以说是牵强的。

如果回调是幂等的并且不需要很多计算能力,则将其触发两次可能是完全不可行的。如果确实多次调用回调,并且想要避免功能检测,则强制将回调仅在回调内运行一次可能会更直接:

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');
}));

51
JS解决方案有缺陷!animate函数执行两次,分别针对html元素和body元素。看起来Webkit浏览器使用body,其余使用html。此修复程序可以完成工作$(jQuery.browser.webkit ? "body": "html").animate(...);
Andreyco 2012年

2
我发现Andreyco的解决方案在我使用的jQuery <1.4中不起作用。我能够这样解决:$(jQuery.browser.mozilla ? "html" : "body").animate(...);。不使用浏览嗅探,但使用特征检测将是很棒的。虽然不确定如何在CSS上进行特征检测
Rustavore

23
请注意,jQuery.browser最新的jQuery版本已弃用和缺失
spiderplant0 2013年

2
4年了,这个答案仍然有用。非常感谢!
马特

1
@DamFa:主要是因为window.scroll没有动画。当然,您可以间隔一定时间滚动自己的东西scrollBy。如果您想增加宽松性,您突然会为产品的次要方面编写大量代码。
David Hedlund

19

特征检测然后在单个受支持对象上设置动画效果会很好,但是还没有一种解决方案。同时,这是一种使用promise每次执行一次回调的方法。

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

更新:这是您可以使用特征检测的方法。在调用动画之前,需要对以下代码进行评估:

// 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();

现在,使用我们刚刚定义为页面滚动动画选择器的var,并使用常规语法:

$(scrollTopElement).animate({ scrollTop: 100 }, 500, function() {
    // normal callback
});

通常情况下效果很好,谢谢!但是,有几个陷阱需要注意。(1)如果在功能测试运行时主体没有足够的内容使窗口溢出,则窗口将不会滚动,并且测试将返回假的“主体”结果。(2)如果测试在iOS的iframe中运行,则由于相同的原因它会失败。iOS中的iframe会垂直扩展(而不是水平扩展),直到内容放进内部而不滚动。(3)因为测试是在主窗口中执行的,所以它会触发已经就绪的滚动处理程序。...(续)
hashchange

...由于这些原因,防弹测试必须在具有足够大内容(解决方案(1))的专用iframe(解决方案(3))中进行,并对其水平滚动(解决方案(2))进行测试。
hashchange

我实际上必须在超时脚本中运行此函数,以使其获得一致的结果,否则它似乎有时会返回html,有时会返回body。这是在声明函数后的代码,var scrollTopElement; setTimeout( function() { scrollTopElement = getScrollTopElement(); console.log(scrollTopElement); }, 1000); 尽管如此,但还是谢谢您,它已经解决了我的问题。
Tony M

没关系,我知道了实际的问题是什么。如果在重新加载(f5)时已经位于页面底部,则此脚本将返回,html而不是body假定的那样。仅当您一直滚动到整个网页并重新加载时,否则它可以工作。
Tony M,

在我添加了页面底部是否打开的检查之后,这对我有用。
斯科特,2016年

6

我花了很长时间试图弄清楚为什么我的代码不起作用-

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

问题出在我的CSS中-

body { height: 100%};

我将其设置为auto(并且一直担心为什么将其设置为100%第一位)。这为我解决了。


2

您可能想通过使用插件来躲避问题-更具体地说,我的插件 :)

认真地讲,即使基本问题早已得到解决(不同的浏览器使用不同的元素进行窗口滚动),但仍有很多非平凡的问题可能会使您绊倒:

我显然有偏见,但是jQuery.scrollable实际上是解决这些问题的一个不错的选择。(实际上,我不知道有其他插件可以处理所有问题。)

此外,您还可以使用此要点中getScrollTargetPosition()功能以防弹方式计算目标位置(滚动至目标位置)。

所有这些都会让你

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

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

    return false;
}

1

当心这一点。我遇到了同样的问题,Firefox或Explorer都无法滚动

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

所以我曾经像大卫所说

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

很好用,但是出现了新问题,因为有两个元素,body和html,函数执行两次,也就是说,X执行两次。

仅使用'html'尝试过,并且Firefox和Explorer可以工作,但现在Chrome不支持此功能。

因此,Chrome需要使用正文,而Firefox和Explorer需要使用html。它是jQuery的错误吗?不知道

请注意您的功能,因为它将运行两次。


您是否为此找到了解决方法?而且由于不赞成使用jQuery浏览器检测,所以我不知道如何使用特征检测来区分chrome和firefox。他们的文档没有指定chrome中存在的功能,而不是firefox中存在的功能,反之亦然。:(
Mandeep Jain

2
那么.stop(true,true).animate呢?
Toni Michel Caubet 2013年

0

我建议不要依赖body也不html是更可移植的解决方案。只需在主体中添加一个div即可包含滚动元素并对其样式进行设置即可启用全尺寸滚动:

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

(假设display:block;top:0;left:0;是与您的目标匹配的默认值),然后将其$('#my-scroll')用于动画。


0

这是实打实的。它可以完美地在Chrome和Firefox上运行。一些无知的人将我投下反对票,这真是令人遗憾。该代码从字面上看可以像在所有浏览器上一样完美运行。您只需要添加一个链接,然后将要滚动的元素的id放入href中即可使用,而无需指定任何内容。纯可重用且可靠的代码。

$(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 [];
  }
});

1
它可能很复杂,但确实有用。它基本上可以在所有浏览器中使用即插即用功能。一个更简单的解决方案可能不允许您这样做。
drjorgepolanco

0

我最近遇到了同样的问题,并通过执行以下操作解决了该问题:

$ ('html, body'). animate ({scrollTop: $ ('. class_of_div'). offset () .top}, 'fast'});

和你!它适用于所有浏览器。

如果定位不正确,您可以通过执行以下操作从offset().top中减去一个值

$ ('html, body'). animate ({scrollTop: $ ('. class_of_div'). offset () .top-desired_value}, 'fast'});

现有的答案中已经提到了此解决方案,但感谢您的分享
Toni Michel Caubet,

正如我刚刚发布的问题所述,该方法在Firefox中仍然遇到故障:stackoverflow.com/q/58617731/1056713
Chaya Cooper

-3

对我来说,问题是firefox会自动跳到锚点,其名称属性与我放入URL中的哈希名称相同。即使我放了.preventDefault()来防止这种情况。因此,更改名称属性后,firefox不会自动跳转到锚点,而是正确执行动画。

@Toni对不起,如果这无法理解。关键是我更改了www.someurl.com/#hashname之类的URL中的哈希。然后我有一个锚,例如<a name="hashname" ...></a>jQuery应该自动滚动到的锚。但这不是因为它在没有任何滚动动画的情况下直接跳到Firefox中具有匹配名称属性的锚点。一旦我将name属性更改为与哈希名称不同的名称(例如,更改为)name="hashname-anchor",滚动就会起作用。


-5

对我来说,避免在动画时附加ID:

避免:

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

事先准备id并执行以下操作:

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

在FF中固定。(css的添加对我没有影响)


-8
setTimeout(function(){                               
                   $('html,body').animate({ scrollTop: top }, 400);
                 },0);

希望这行得通。


4
setTimeut在这里有何帮助?
托尼·米歇尔·考贝特

2
@ToniMichelCaubet的目标大概是使动画异步。jQuery动画已经是异步的,因此它什么也做不了。
mwcz 2014年

不是对问题的深思熟虑的答案。不知道这将完成什么。
erier 2016年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.