Получение всей DOM-оболочки, ограничивающей прямоугольник клиента - PullRequest
1 голос
/ 03 ноября 2019

Я ищу способ получить виртуальную ограничивающую рамку, которая обернет все элементы DOM.

Например, 2 деления с 100X100 px размерами с position absolute один размещен в top:0 left :0, а другой в top:700px left:700px приведет к 800X800 прямоугольнику (изображение 1):

enter image description here

При прокрутке я ожидаю, что все ещеполучить 800X800 прямоугольник со смещением прокручиваемого расстояния (изображение 2): enter image description here

Я думаю об итерациях по 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
    });
  }

Ответы [ 2 ]

1 голос
/ 04 ноября 2019

Я написал свой собственный метод:

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
    });
  }
0 голосов
/ 03 ноября 2019

Если вы хотите использовать абсолютно позиционированные элементы, это невозможно, поскольку они удаляются из потока DOM, как указано здесь .

Это необходимо сделать вручную, точно так же, какВы говорите.

Я создал небольшую скрипку, чтобы поиграть с этим (щелкните элементы, чтобы изменить положение дочерних элементов относительно, чтобы увидеть, как расширяется контейнер):

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