Animate scrollTopがFirefoxで機能しない


164

この機能は正常に動作します。本体を目的のコンテナのオフセットまでスクロールします

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

しかし、Firefoxにはありません。どうして?

-編集-

受け入れられた回答でde doubleトリガーを処理するには、アニメーションの前に要素を停止することをお勧めします。

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

2
最終的なコードは、ここにあるものの中で最もエレガントです。
Lizardx 2017

回答:


331

Firefoxはhtml、特に異なる動作をするようにスタイル設定されていない限り、オーバーフローをレベルに配置します。

Firefoxで動作させるには、

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

実施例

CSSソリューションは、次のスタイルを設定することです。

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

JSソリューションは最も侵襲性が低いと思います。


更新

以下の説明の多くは、scrollTop2つの要素をアニメーション化するとコールバックが2回呼び出されるという事実に焦点を当てています。ブラウザ検出機能が提案され、その後非推奨になりました、そして、いくつかは間違いなくかなり遠く離れています。

コールバックがべき等であり、大量の計算能力を必要としない場合、2回コールバックすることは完全に問題ではない可能性があります。コールバックの複数の呼び出しが本当に問題であり、機能の検出を避けたい場合は、コールバックがコールバック内から1回だけ実行されるように強制する方が簡単です。

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要素のそれぞれに対して2回実行されます。Webkitブラウザーはbodyを使用し、残りはhtmlを使用しているようです。この修正でうまくいくはずです$(jQuery.browser.webkit ? "body": "html").animate(...);
Andreyco

2
Andreycoのソリューションが、使用しているjQuery <1.4では機能しないことがわかりました。次のように修正できました:$(jQuery.browser.mozilla ? "html" : "body").animate(...);。ブラウズスニッフィングを使用せず、機能検出を使用するのは素晴らしいことです。ただし、CSSで機能を検出する方法がわからない
Rustavore

23
注、jQuery.browser非推奨であり、最新のjQueryバージョンから欠落しています
spiderplant0

2
4年経ちましたが、この答えはまだ有用であることが証明されています。どうもありがとう!
Matt

1
@DamFa:主に、window.scrollアニメーション化しないため。もちろん、間隔を空けて自分のものを転がすこともできますscrollBy。イージングを追加したい場合は、突然、製品のマイナーな側面を記述するための大量のコードが作成されます。
David Hedlund 2017

19

機能を検出し、サポートされている単一のオブジェクトでアニメーション化するのは良いことですが、1行の解決策はありません。それまでの間、Promiseを使用して実行ごとに1つのコールバックを実行する方法を次に示します。

$('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();

次に、先ほど定義した変数をページスクロールアニメーションのセレクターとして使用し、通常の構文を使用します。

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

通常の状況でうまく機能します、ありがとう!ただし、注意すべきいくつかの落とし穴があります。(1)機能テストの実行時にボディにウィンドウをオーバーフローするのに十分なコンテンツがない場合、ウィンドウはスクロールせず、テストは偽の「ボディ」結果を返します。(2)テストがiOSのiframe内で実行される場合、同じ理由で失敗します。iOSのiframeは、コンテンツがスクロールせずに内部に収まるまで(水平ではなく)垂直方向に拡大します。(3)テストはメインウィンドウで実行されるため、既に配置されているスクロールハンドラーをトリガーします。...(続き)
ハッシュ変更2015

...これらの理由から、防弾試験は、水平スクロール(解決(2))についてテストされる十分に大きなコンテンツ(解決(1))を含む専用iframe(解決(3))内で実行する必要があります。
hashchangeは2015

これを一貫した結果にするために、実際にはこの関数をタイムアウトスクリプトで実行する必要がhtmlありましたbody。関数を宣言した後のコードは次のとおりです。var scrollTopElement; setTimeout( function() { scrollTopElement = getScrollTopElement(); console.log(scrollTopElement); }, 1000); ありがとうございます。問題は解決しました。
Tony M

気にしないで、私は実際の問題が何であるかを理解しました。リロード(f5)したときにすでにページの一番下にいる場合、このスクリプトは想定したようにではhtmlなく戻りbodyます。これは、ウェブページを一番下までスクロールしてリロードした場合のみで、それ以外の場合は機能します。
トニーM

これは、ページが下部に開いているかどうかのチェックを追加した後で機能しました。
スコット

6

私は何年も費やして、コードが機能しない理由を解明しようとしました-

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

問題は私のCSSにありました-

body { height: 100%};

私はauto代わりにそれを設定しました(そして、なぜそれが100%最初に設定されたのかについて心配していました)。これで解決しました。


2

プラグインを使用して問題を回避することができます。具体的には、私のプラグイン :)

深刻なことに、基本的な問題はずっと以前から対処されていますが(ブラウザによってはウィンドウのスクロールにさまざまな要素を使用します)、かなりの数の重要な問題が発生します。

私は明らかに偏っていますが、jQuery.scrollableは実際にはこれらの問題に対処するための良い選択です。(実際、私はそれらすべてを処理する他のプラグインを知りません。)

あなたがスクロール1 - -と防弾方法で、また、あなたは目標位置を計算することができ、この主旨で関数をgetScrollTargetPosition()

そのすべてがあなたを残すでしょう

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

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

    return false;
}

1

これに注意してください。私も同じ問題を抱えていました

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

だから私はデビッドが言ったように使用しました

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

うまくいきましたが、新しい問題です。bodyとhtmlの2つの要素があるため、関数が2回実行されます。つまり、Xが2回実行されます。

「html」のみで試してみましたが、FirefoxとExplorerは機能しますが、Chromeではこれをサポートしていません。

したがって、Chromeにはbody、FirefoxとExplorerにはhtmlが必要でした。それはjQueryのバグですか?分からない。

関数は2回実行されるため、関数には注意してください。


これの回避策を見つけましたか?また、jqueryブラウザー検出は非推奨であるため、機能検出を使用してchromeとfirefoxを区別する方法を知りません。彼らのドキュメンテーションはクロムに存在する機能を明記しておらず、Firefoxまたはその逆にはありません。:(
Mandeep Jain 2013

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
複雑かもしれませんが、それは本当に便利です。基本的に、すべてのブラウザでPlug-N-Playが機能します。より単純なソリューションでは、それができない場合があります。
drjorgepolanco 2015年

0

私は最近同じ問題に遭遇し、これを行うことで解決しました:

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

そしてyoupi !!! すべてのブラウザで動作します。

配置が正しくない場合は、これを実行してオフセット().topから値を減算できます

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

この解決策はすでに既存の回答で言及されていますが、共有してくれてありがとう
Toni Michel Caubet

質問で述べたように、この方法はまだFirefoxでグリッチが発生した、私はちょうど投稿:stackoverflow.com/q/58617731/1056713
茶屋クーパー

-3

私にとっての問題は、FirefoxがURLに入力したハッシュ名と同じ名前属性を持つアンカーに自動的にジャンプすることでした。それを防ぐために.preventDefault()を置いたとしても。したがって、name属性を変更した後、Firefoxは自動的にアンカーにジャンプせず、アニメーションを正しく実行しました。

@Toniこれが理解できない場合はごめんなさい。問題は、URLのハッシュをwww.someurl.com/#hashnameのように変更したことです。次に、たとえば、<a name="hashname" ...></a>jQueryが自動的にスクロールするようなアンカーがありました。しかし、スクロールアニメーションなしで、Firefoxで一致するname属性を持つアンカーに直接ジャンプしたため、そうなりませんでした。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はどのように役立ちますか?
Toni Michel Caubet 2013年

2
@ToniMichelCaubetはおそらく、アニメーションを非同期にすることを目的としています。ただし、jQueryアニメーションはすでに非同期であるため、これは何も実行しません。
mwcz 2014

問題に対する思慮深い答えではありません。これが何を達成するかわかりません。
erier 2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.