web-dev-qa-db-ja.com

スクロール中のJavaScript getBoundingClientRect()の変更

要素のY座標とY値= 0の間の正確な距離が必要です。これをドキュメントの最上部と見なします。

myElement.getBoundingClientRect().top;

しかし、getBoundingClientRect()の値はスクロール中に変化するようです。 myElementとY座標= 0(ドキュメントの上部)間の実際の距離を取得するにはどうすればよいですか?

57
enzian

これは、getBoundingClientRect()window(ページ全体)ではなく、document(ページの現在表示されている部分のみ)に関して値を取得するためです。
したがって、値の計算時にスクロールも考慮されます
基本的に、document = window + scroll

したがって、myElementとY座標= 0(ドキュメントの上部)の間の距離を取得するには、垂直スクロールの値も追加する必要があります。

myElement.getBoundingClientRect().top + window.scrollY;

ソース: https://developer.mozilla.org/en-US/docs/Web/API/Element.getBoundingClientRect

86
n4m31ess_c0d3r

getBoundingClientRectは、scrollY/pageYOffsetのバグを回避するためにもう少し注意が必要です。

function absolutePosition(el) {
    var
        found,
        left = 0,
        top = 0,
        width = 0,
        height = 0,
        offsetBase = absolutePosition.offsetBase;
    if (!offsetBase && document.body) {
        offsetBase = absolutePosition.offsetBase = document.createElement('div');
        offsetBase.style.cssText = 'position:absolute;left:0;top:0';
        document.body.appendChild(offsetBase);
    }
    if (el && el.ownerDocument === document && 'getBoundingClientRect' in el && offsetBase) {
        var boundingRect = el.getBoundingClientRect();
        var baseRect = offsetBase.getBoundingClientRect();
        found = true;
        left = boundingRect.left - baseRect.left;
        top = boundingRect.top - baseRect.top;
        width = boundingRect.right - boundingRect.left;
        height = boundingRect.bottom - boundingRect.top;
    }
    return {
        found: found,
        left: left,
        top: top,
        width: width,
        height: height,
        right: left + width,
        bottom: top + height
    };
}

回避すべきバグは次のとおりです。

  • Android ChromeのスクロールChrome Mobile 43には 間違った値 scrollY/pageYOffsetがあります(特にキーボードが表示されてスクロールしている場合) )。

  • Microsoft IEまたはEdgeのピンチズームにより、scrollY/pageYOffsetで 間違った値 が発生します。

  • 一部の(廃止された)ブラウザーには高さ/幅がありません。 IE8

編集:上記のコードは、divを追加する代わりにdocument.body.getBoundingClientRect()を使用するだけで大​​幅に簡素化できます。まだ試していませんので、答えをそのままにしておきます。また、本文にはmargin:0が必要です(通常、reset.cssがこれを行います)。 この回答 jQuery.offset()のバグを回避しながら、コードを大幅に簡素化します!

編集2: Chrome 61ではwindow.visualViewport を導入して、実際のビューポートに正しい値を与えました。これはおそらく問題を修正する別の方法です。ただし、Android Chrome 66は、Settings -> Accessability -> Force enable zoomにチェックマークが付いている場合はバグが残ることに注意してください(ビューポートよりも広い位置にポップアップが配置され、方向が変わるバグ)。

29
robocat