0

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
    });
  }
Shlomi Schwartz
  • 8,693
  • 29
  • 109
  • 186

2 Answers2

0

If you want to go with absolute positioned elements, that's impossible, as they are removed from the DOM flow, as stated here.

You need to do that manually, exactly as you say.

I created a small fiddle to play with this (click elements to change children postion to relative to see how container expands):

var container = document.createElement("div");
container.classList.add("cont");

var a = document.createElement("div")
a.classList.add("abs");

var b = document.createElement("div")
b.classList.add("abs");
b.style.left = b.style.top = "100px";

container.appendChild(a);
container.appendChild(b);
document.body.appendChild(container);

document.body.addEventListener("click", () => {
    a.style.position = b.style.position = a.style.position ? "" : "relative";
});
html, body {
    margin: 0;
    padding: 0;
}
.cont {
    position: absolute;
    display: inline;
    background-color: blue;
}
.abs {
    position: absolute;
    width: 50px;
    height: 50px;
    background-color: red;
}
Jorge Fuentes González
  • 11,568
  • 4
  • 44
  • 64
0

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
    });
  }
Shlomi Schwartz
  • 8,693
  • 29
  • 109
  • 186