Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript getBoundingClientRect() changes while scrolling

I want to have the exact distance between the Y-coordinate of an element an the Y-value=0, which I consider as the top of the document.

myElement.getBoundingClientRect().top;

But the value of getBoundingClientRect() seems to change while scrolling. How can I get the real distance between myElement and the Y-coordinate=0 (top of document)?

like image 228
enzian Avatar asked Sep 02 '14 18:09

enzian


People also ask

What is getBoundingClientRect () in Javascript?

The getBoundingClientRect() method returns the size of an element and its position relative to the viewport. The getBoundingClientRect() method returns a DOMRect object with eight properties: left, top, right, bottom, x, y, width, height.

Does getBoundingClientRect include border?

Border, padding and margin are not included. This means when you set a width, That width is set to the content only then you add the padding and border.

What is getBoundingClientRect () Top?

The Element.getBoundingClientRect() method returns a DOMRect object providing information about the size of an element and its position relative to the viewport.

Does getBoundingClientRect work on mobile?

getBoundingClientRect() on Android Browser is fully supported on 2.3-103, partially supported on None of the versions, and not supported on below 2.3 Android Browser versions.


2 Answers

It is because getBoundingClientRect() gets values with respect to the window(only the current visible portion of the page), not the document(whole page).
Hence, it also takes scrolling into account when calculating its values
Basically, document = window + scroll

So, to get the distance between myElement and the Y-coordinate=0 (top of document), you would have add the value of vertical-scroll also:

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

Source: https://developer.mozilla.org/en-US/docs/Web/API/Element.getBoundingClientRect

like image 138
n4m31ess_c0d3r Avatar answered Oct 13 '22 20:10

n4m31ess_c0d3r


getBoundingClientRect needs a bit more care to avoid bugs in 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
    };
}

The bugs to avoid are:

  • scrolling in Android Chrome since Chrome Mobile 43 has wrong values for scrollY/pageYOffset (especially when the keyboard is showing and you scroll).

  • Pinch-zoom in Microsoft IE or Edge causes wrong values for scrollY/pageYOffset.

  • Some (obsolete) browsers don't have a height/width e.g. IE8

Edit: The above code can be simplified a lot by just using document.body.getBoundingClientRect() instead of adding a div - I haven't tried it though so I am leaving my answer as it stands. Also the body needs margin:0 (reset.css usually does this). This answer simplifies the code down a lot, while still avoiding the bugs in jQuery.offset()!

Edit 2: Chrome 61 introduced window.visualViewport to give correct values for the actual viewport which is probably another way to fix issues; but beware that Android Chrome 66 was still buggy if Settings -> Accessability -> Force enable zoom was ticked (bugs with orientation change, focused inputs, absolutely positioned popup wider than viewport).

like image 34
robocat Avatar answered Oct 13 '22 21:10

robocat