If you can identify the scrollable container which your element rests in, you can simply add its vertical scroll offset (scrollTop
) to the result of getBoundingClientRect().top
.
On the other hand, if you have no idea which container in your element's parent tree is scrollable, or if your element has more than one scrollable parent, you can check your element's tree up to the top node, which is the <html>
element, i.e. document.documentElement
.
function getPageOffset(elem) {
let topOffset = elem.getBoundingClientRect().top;
while (elem != document.documentElement) {
elem = elem.parentElement;
topOffset += elem.scrollTop;
}
return topOffset;
}
Here is a simple example of a paragraph nested in a tree of three scrollable containers, which makes use of the above function (you may need to resize your browser's viewport for the scrollbars to appear):
var myElem = document.getElementById("myElem");
function getPageOffset(elem) {
let topOffset = elem.getBoundingClientRect().top;
while (elem != document.documentElement) {
elem = elem.parentElement;
topOffset += elem.scrollTop;
}
return topOffset;
}
document.getElementById("myBtn").addEventListener("click", function() {
console.log(getPageOffset(myElem));
});
.parent1,
.parent2,
.parent3 {
height: 100vh;
overflow-y: auto;
}
.parent1 {
height: 400px;
}
button {
position: fixed;
top: 20px;
left: 20px;
}
<div class="parent1">
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<div class="parent2">
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<div class="parent3">
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
<p id="myElem">This is the element I want to get the top offset of.</p>
</div>
</div>
</div>
<button id="myBtn">Get top offset</button>
There are negligible margins of error between the measurements (due to addition inaccuracies) which can be overcome with some sort of rounding.