I’m interested in getting the bounding rect of a HTML element within an scrolling (overflow:auto
) div container. I’ve tried getBoundingClientRect()
but this is always relative to the window. A DOMRect
is not needed, I just wanna know the width of the viewport and the position of the element.
So for me the only solution seems to be to subtract the rect of the scrolling div. But this could get complicated as my final use case is working with shadow DOM and custom components.
<div style="overflow:auto;height:100px;position:absolute;top:50px;"> <div id="elem"> some content </div> <div style="height:100px;"> ignore </div> <div> ignore </div> <div> ignore </div> </div> <script> window.alert("top="+document.getElementById("elem").getBoundingClientRect().top); </script>
In this example you can see the outer most div has overflow
set but the bounding rect does not show 0 before scrolling but 50.
Example: https://jsfiddle.net/nvemtoyk/
Advertisement
Answer
Found a workaround but with some caveats. First, you have to traverse all parent elements until you find the viewport. Secondy, It’s only working if the overflow div was already scrolled.
At least in my case the second is true because the overflow
style is not visible in javascript at my custom element. Maybe in “legacy” HTML this is not the case.
getViewport(elem) { // root element if (elem === document.body) { return document.documentElement; } // scrolling element (only working if already scrolled) // maybe elem.style.overflow is available, but not in my case else if (elem.scrollLeft > 0 || elem.scrollTop > 0) { return elem; } // traverse else { return getViewport(elem.offsetParent); } } getBoundingRect(element, viewport) { // root element if (viewport === document.documentElement) { return element.getBoundingClientRect(); } // relative to viewport else { var elRect = element.getBoundingClientRect(); var vpRect = viewport.getBoundingClientRect(); return { bottom: elRect.bottom - vpRect.top, height: elRect.height, left: elRect.left - vpRect.left, right: elRect.right - vpRect.left, top: elRect.top - vpRect.top, width: elRect.width }; } }