Search code examples
javascripthtmldombounding-box

Getting entire DOM wrapping bounding client rect


I'm looking for a way to obtain a virtual bounding box that will wrap inside all the DOM elements.

For example, 2 divs with 100X100 px dimensions with position absolute one is placed at top:0 left :0 and the other is placed at top:700px left:700px will result a 800X800 rect (image 1):

enter image description here

When scrolled I expect to still get an 800X800 rectangular with an offset of the scrolled distance (image 2): enter image description here

I'm thinking of iterating through the DOM, getting all bounding client rectangulars and calculate manually, something like so:

document.querySelectorAll("*").forEach((el)=>{
  let r = el.getBoundingClientRect();
  //calculate
})

However, it seems not very efficient. Any help would be appreciated.

Update: This is the code so far, any insights would be appreciated:

function getDocumentVisualBoundingBox() {
    return Array.prototype.reduce.call(document.querySelectorAll("*"), (res, el) => {
    //Looking at BODY element, Absolute positioned elements and ignoring elements within scrollable containers.
      if (el.tagName === 'BODY' || (el.parentElement && getComputedStyle(el.parentElement).overflow === 'visible' && getComputedStyle(el).position === 'absolute')) {
        let rect = el.getBoundingClientRect();
        res.offsetLeft = Math.min(res.offsetLeft, rect.left);
        res.offsetTop = Math.min(res.offsetTop, rect.top);
        res.width = Math.max(res.width, rect.width + Math.abs(res.offsetLeft) + rect.left);
        res.height = Math.max(res.height, rect.height + Math.abs(res.offsetTop) + rect.top);
      }
      return res;
    }, {
      offsetLeft: 0,
      offsetTop: 0,
      width: 0,
      height: 0
    });
  }

Solution

  • I've ended up writing my own method:

    function getDocumentVisualBoundingBox() {
        return Array.prototype.reduce.call(document.querySelectorAll("*"), (res, el) => {
        //Looking at BODY element, Absolute positioned elements and ignoring elements within scrollable containers.
          if (el.tagName === 'BODY' || (el.parentElement && getComputedStyle(el.parentElement).overflow === 'visible' && getComputedStyle(el).position === 'absolute')) {
            let rect = el.getBoundingClientRect();
            res.offsetLeft = Math.min(res.offsetLeft, rect.left);
            res.offsetTop = Math.min(res.offsetTop, rect.top);
            res.width = Math.max(res.width, rect.width + Math.abs(res.offsetLeft) + rect.left);
            res.height = Math.max(res.height, rect.height + Math.abs(res.offsetTop) + rect.top);
          }
          return res;
        }, {
          offsetLeft: 0,
          offsetTop: 0,
          width: 0,
          height: 0
        });
      }