获取整个 DOM 包装边界客户端矩形

Getting entire DOM wrapping bounding client rect

我正在寻找一种方法来获得一个虚拟边界框,它将包裹在所有 DOM 元素中。

例如,2 个 100X100 px 尺寸的 div,其中一个 position absolute 放置在 top:0 left :0,另一个放置在 top:700px left:700px 将导致 800X800 矩形(图片 1):

滚动时,我希望仍会得到一个 800X800 矩形,其中包含滚动距离的偏移量(图 2):

我正在考虑遍历 DOM,获取所有边界客户端矩形并手动计算,如下所示:

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

不过,好像效率不是很高。任何帮助,将不胜感激。

更新: 这是到目前为止的代码,任何见解将不胜感激:

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
    });
  }

如果您想使用绝对定位元素,那是不可能的,因为它们已从 DOM 流程中删除,如 here.

所述

您需要手动执行此操作,完全按照您说的去做。

我创建了一个小 fiddle 来玩这个(单击元素将子元素位置更改为相对位置以查看容器如何扩展):

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;
}

我最终编写了自己的方法:

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
    });
  }