【问题标题】:Animate scrollTop not working in firefox动画 scrollTop 在 Firefox 中不起作用
【发布时间】:2011-12-30 06:04:58
【问题描述】:

这个功能很好用。它将正文滚动到所需容器的偏移量

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

【问题讨论】:

  • 你的最终代码是这里所有代码中最优雅的。

标签: jquery firefox scrolltop


【解决方案1】:

Firefox 将溢出设置在 html 级别,除非特别设置为不同的行为。

要让它在 Firefox 中运行,请使用

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

Working example

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

【讨论】:

  • JS方案有缺陷! animate 函数执行两次,分别针对 html 元素和 body 元素。看起来 Webkit 浏览器使用 body,其余的使用 html。此修复程序应该可以完成工作$(jQuery.browser.webkit ? "body": "html").animate(...);
  • 我发现 Andreyco 的解决方案在我使用的 jQuery $(jQuery.browser.mozilla ? "html" : "body").animate(...);。最好不要使用浏览嗅探,而是使用特征检测。不知道如何在 CSS 上进行特征检测
  • 注意,jQuery.browser 在最新的 jQuery 版本中已被弃用和缺失
  • 4 年过去了,这个答案仍然被证明是有用的。非常感谢!
  • @DamFa:主要是因为window.scroll 没有动画。你当然可以用间隔和scrollBy滚动你自己的东西。如果您想为此添加缓动,您突然需要为产品的一个相当小的方面编写大量代码。
【解决方案2】:

特征检测然后在单个受支持的对象上制作动画会很好,但没有单线解决方案。同时,这里有一种使用 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) 因为测试是在主窗口中执行的,所以它会触发已经存在的滚动处理程序。 ...(续)
  • ... 由于这些原因,防弹测试必须在专用的 iframe(解决 (3))内运行,其中包含足够大的内容(解决 (1)),用于测试水平滚动 (解决 (2))。
  • 我实际上必须在超时脚本中运行这个函数才能得到一致的结果,否则它有时会返回html,有时会返回body。这是我声明函数var scrollTopElement; setTimeout( function() { scrollTopElement = getScrollTopElement(); console.log(scrollTopElement); }, 1000);后的代码,不过谢谢你,它解决了我的问题。
  • 没关系,我知道真正的问题是什么。如果您在重新加载 (f5) 时已经在页面底部,则此脚本将返回 html 而不是 body 就像它应该的那样。仅当您一直向下滚动网页并重新加载时,它才会起作用。
  • 在我添加检查页面是否在底部打开后,这对我有用。
【解决方案3】:

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

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

问题出在我的 CSS 中 -

body { height: 100%};

我改为将其设置为auto(并且首先担心为什么将其设置为100%)。这为我解决了问题。

【讨论】:

    【解决方案4】:

    您可能想通过使用插件来避开这个问题 - 更具体地说,my plugin :)

    说真的,尽管基本问题早已得到解决(不同的浏览器使用不同的元素来滚动窗口),但仍有不少不平凡的问题可能会让您大吃一惊:

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

    此外,您可以使用the getScrollTargetPosition() function in this gist 以防弹的方式计算目标位置(您滚动到的位置)。

    所有这些都会留给你

    function scrolear ( destino ) {
        var $window = $( window ),
            targetPosition = getScrollTargetPosition ( $( destino ), $window );
    
        $window.scrollTo( targetPosition, { duration: 1000 } );
    
        return false;
    }
    

    【讨论】:

      【解决方案5】:

      请注意这一点。 我遇到了同样的问题,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 需要 body,Firefox 和 Explorer 需要 html。它是一个 jQuery 错误吗?不知道。

      请注意您的函数,因为它会运行两次。

      【讨论】:

      • 你找到解决方法了吗?而且由于不推荐使用jquery浏览器检测,我不知道如何使用特征检测来区分chrome和firefox。他们的文档没有指定 chrome 中存在的功能,而不是 firefox 中的功能,反之亦然。 :(
      • .stop(true,true).animate 怎么样?
      【解决方案6】:

      我建议不要依赖 bodyhtml 作为更便携的解决方案。只需在 body 中添加一个 div,该 div 旨在包含滚动元素并设置其样式以启用全尺寸滚动:

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

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

      【讨论】:

        【解决方案7】:

        这是真正的交易。它可以在 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 [];
          }
        });
        

        【讨论】:

        • 它可能很复杂,但它确实很有用。它基本上可以在所有浏览器中即插即用。更简单的解决方案可能不允许您这样做。
        【解决方案8】:

        我最近遇到了同样的问题,我通过这样做解决了它:

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

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

        万一定位不正确,可以这样从offset().top中减去一个值

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

        【讨论】:

        • 现有答案中已经提到了这个解决方案,但感谢分享
        • 这种方法在 Firefox 中仍然会遇到故障,正如我刚刚发布的问题中所述:stackoverflow.com/q/58617731/1056713
        【解决方案9】:

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

        @Toni 抱歉,如果这无法理解。问题是我更改了 URL 中的哈希值,例如 www.someurl.com/#hashname。然后我有一个像&lt;a name="hashname" ...&gt;&lt;/a&gt; 这样的锚点,jQuery 应该自动滚动到该锚点。但它没有,因为它在没有任何滚动动画的情况下直接跳转到 Firefox 中具有匹配名称属性的锚点。一旦我将 name 属性更改为与哈希名称不同的名称,例如 name="hashname-anchor",滚动就起作用了。

        【讨论】:

          【解决方案10】:

          对我来说,这是避免在动画点附加 ID:

          避免:

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

          事先准备好 id 并改为这样做:

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

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

          【讨论】:

            【解决方案11】:
            setTimeout(function(){                               
                               $('html,body').animate({ scrollTop: top }, 400);
                             },0);
            

            希望这行得通。

            【讨论】:

            • setTimeut 在这里有什么帮助?
            • @ToniMichelCaubet 的目标大概是使动画异步。不过,jQuery 动画已经是异步的了,所以这什么也做不了。
            • 对这个问题没有深思熟虑的答案。不确定这会完成什么。
            猜你喜欢
            • 2015-01-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-02-20
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多