jQuery 动画 scrollTop 在 Firefox 中不起作用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8149155/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 10:07:29  来源:igfitidea点击:

Animate scrollTop not working in firefox

jqueryfirefoxscrolltop

提问by Toni Michel Caubet

This function works fine. It scrolls the body to a desired container's offset

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

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

But not in Firefox. Why?

但不是在 Firefox 中。为什么?

-EDIT-

-编辑-

To handle de double trigger in the accepted answer, I suggest stoping the element before the animation:

要在接受的答案中处理 de double trigger,我建议在动画之前停止元素:

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

回答by dxh

Firefox places the overflow at the htmllevel, unless specifically styled to behave differently.

Firefox 将溢出放置在html级别,除非经过特别设计以使其行为不同。

To get it to work in Firefox, use

要让它在 Firefox 中工作,请使用

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

Working example

工作示例

The CSS solution would be to set the following styles:

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

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

I would assume that the JS solution would be least invasive.

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



Update

更新

A lot of the discussion below focuses on the fact that animating the scrollTopof two elements would cause the callback to be invoked twice. Browser-detection features have been suggested and subsequently deprecated, and some are arguably rather far-fetched.

下面的很多讨论都集中在scrollTop对两个元素进行动画处理会导致回调被调用两次这一事实。浏览器检测功能已被建议并随后被弃用,其中一些可以说是相当牵强。

If the callback is idempotent and doesn't require a lot of computing power, firing it twice may be a complete non-issue. If multiple invocations of the callback are truly an issue, and if you want to avoid feature-detection, it might be more straight-forward to enforce that the callback is only run once from within the 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');
}));

回答by Stephen

Feature detection and then animating on a single supported object would be nice, but there's not a one line solution. In the meantime, here's a way to use a promise to do a single callback per execution.

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

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

UPDATE: Here's how you could use feature detection instead. This chunk of code needs to get evaluated before your animation call:

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

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

Now use the var that we just defined as the selector for the page scrolling animation, and use the regular syntax:

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

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

回答by Aidan Ewen

I spent ages trying to work out why my code wouldn't work -

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

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

The problem was in my css -

问题出在我的 css 中 -

body { height: 100%};

I set it to autoinstead (and was left worrying about why it was set to 100%in the first place). That fixed it for me.

我将其设置为auto(并且首先担心为什么将其设置为100%)。那为我修好了。

回答by hashchange

You might want to dodge the issue by using a plugin – more specifically, my plugin:)

你可能想通过使用插件来避免这个问题——更具体地说,我的插件:)

Seriously, even though the basic problem has long since been addressed (different browsers use different elements for window scrolling), there are quite a few non-trivial issues down the line which can trip you up:

说真的,即使这个基本问题早已得到解决(不同的浏览器使用不同的元素来滚动窗口),但仍有很多重要的问题可能会让你失望:

I'm obviously biased, but jQuery.scrollableis actually a good choice to address these issues. (In fact, I don't know of any other plugin which handles them all.)

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

In addition, you can calculate the target position – the one which you scroll to – in a bullet-proof way with the getScrollTargetPosition()function in this gist.

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

All of which would leave you with

所有这些都会给你留下

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

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

    return false;
}

回答by Avenida Gez

Beware of this. I had the same problem, neither Firefox or Explorer scrolling with

小心这一点。我遇到了同样的问题,Firefox 或 Explorer 都没有滚动

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

So I used like David said

所以我像大卫说的那样使用

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

Great it worked, but new problem, since there are two elements, body and html, function is executed twice, this is, do X runs two times.

很好,但新问题,因为有两个元素,body 和 html,函数被执行两次,也就是,x 运行两次。

tried only with 'html', and Firefox and Explorer work, but now Chrome does not support this.

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

So needed body for Chrome, and html for Firefox and Explorer. Is it a jQuery bug? don't know.

所以 Chrome 需要 body,Firefox 和 Explorer 需要 html。这是一个jQuery错误吗?不知道。

Just beware of your function, since it will run twice.

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

回答by Balthazar DOSSOU

I encountered the same problem just recently and I solved it by doing this:

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

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

And youpi !!! it works on all browsers.

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

in case the positioning is not correct, you can subtract a value from the offset () .top by doing this

如果定位不正确,您可以通过这样做从偏移量 () .top 中减去一个值

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

回答by Javarome

I would recommend notrelying on bodynor htmlas a more portable solution. Just add a div in the body that aims to contain the scrolled elements and style it like to enable full-size scrolling:

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

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

(assuming that display:block;and top:0;left:0;are defaults that matches your goal), then use $('#my-scroll')for your animations.

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

回答by drjorgepolanco

This is the real deal. It works on Chrome and Firefox flawlessly. It is even sad that some ignorant vote me down. This code literally works perfectly as is on all browsers. You only need to add a link and put the id of the element you want to scroll in the href and it works without specifying anything. Pure reusable and reliable code.

这是实打实的。它可以在 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 [];
  }
});

回答by Peter

For me the problem was that firefox automatically jumped to the anchor with the name-attribute the same as the hash name I put into the URL. Even though I put .preventDefault() to prevent that. So after changing the name attributes, firefox did not automatically jump to the anchors, but perform the animation right.

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

@Toni Sorry if this wasn't understandable. The thing is I changed the hashes in the URL like www.someurl.com/#hashname. Then I had for example an anchor like <a name="hashname" ...></a>to which jQuery should scroll to automatically. But it didn't because it jumped right to the anchor with the matching name attribute in Firefox without any scroll animation. Once I changed the name attribute to something different from the hash name, for example to name="hashname-anchor", the scrolling worked.

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

回答by bcm

For me, it was avoiding appending the ID at the point of animation:

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

Avoiding:

避免:

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

Preparing the id beforehand and doing this instead:

事先准备好 id 并改为执行此操作:

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

Fixed in FF. (The css additions didn't make a difference for me)

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