Skip to content
Advertisement

Bounding rect of HTML element within scrolling element

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
      };
    }
  }
User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement