Расположение элементов в HTML-документе

При разметке HTML-страницы можно использовать два принципиально разных способа позиционирования элемента поверх остальных элементов:

  • position: fixed – позиционирование относительно окна браузера, не зависящее от прокрутки документа;
  • position: absolute – позиционирование относительно документа (зависит от величины прокрутки).

Чтобы были понятнее эти различия, приведу такой пример. Пусть у нас имеется вот такой документ:

<!DOCTYPE html>
<html>
<head>
<title>Уроки по JavaScript</title>
<style>
#wnd {z-index: 999; position: fixed; width: 200px; 
   overflow: hidden;background: #CC0000;color: #eee;
   left: 100px;top: 50px;text-align: center;
}
</style>
</head>
<body>
<h1>Позиционирование элементов на странице</h1>
<div id="wnd"><p>Элемент с позиционированием fixed</div>
<ul style="font-size: 20px">
<li>offsetParent<li>offsetLeft<li>offsetTop
<li>offsetWidth<li>offsetHeight<li>clientTop
<li>clientLeft<li>clientWidth<li>clientHeight
<li>scrollWidth<li>scrollHeight<li>scrollLeft
<li>scrollTop<li>innerHeight<li>innerWidth
</ul>
<script>
</script>
</body>
</html>

Если мы его отобразим в окне браузера, то увидим красное окно (элемент div) поверх всей страницы с положением, не зависящем от скроллинга. Но, если заменить стиль на:

position: absolute

то при прокрутке элемент начинает также смещаться вместе со всей страницей. Фактически, мы здесь имеем следующую картину (см. рисунок). В режиме fixed координаты left и top верхнего левого угла элемента div отсчитываются от границ клиентской области окна браузера (обозначим эти параметры через clientX, clientY). В режиме absolute отсчет идет от начала документа – параметры pageX, pageY.

Теперь поставим перед собой такую задачу: нужно с помощью скрипта расположить наш блок div точно по центру окна браузера. Если мы запишем такую программу:

let wnd = document.getElementById("wnd");
let centerX = document.documentElement.clientWidth/2;
let centerY = document.documentElement.clientHeight/2;
wnd.style.left = centerX + "px";
wnd.style.top = centerY + "px";

То по центру будет верхний левый угол, а не сам блок. Здесь из значений centerX, centerY нужно еще вычесть половину ширины и высоты блока div. Эти величины возьмем из свойств offsetWidth/offsetHeight нашего элемента, получим:

wnd.style.left = centerX-wnd.offsetWidth/2 + "px";
wnd.style.top = centerY-wnd.offsetHeight/2 + "px";

Все, теперь блок встал точно по центру окна. Так вот, если мы теперь захотим узнать все координаты и размеры блока div, то всю эту информацию можно получить с помощью метода

let coords = elem.getBoundingClientRect();

Например, добавим строчки:

let coords = wnd.getBoundingClientRect();
console.log(coords);

И обновим документ. В консоле увидим параметры:

x/y, top/left, width/height, bottom/right

Все они представлены на рисунке ниже. Причем,

right = left+width
bottom = top+height

Использовать эти методы бывает очень удобно для получения актуальных значений координат и размеров какого-либо элемента-тега в HTML-документе.

Здесь вы можете заметить два дублирующих свойства: top = y и left = x. Зачем нужны x и y? Дело в том, что при отрицательных значениях width или height значения x и y будут соответствовать нижнему правому углу, а top и left продолжат указывать на верхний левый. Правда такие моменты редко используются на практике, да и некоторые браузеры не возвращают величины x/y (например, IE и Edge).

Также следует иметь в виду, что параметры right и bottom не имеют ничего общего с аналогичными параметрами в CSS. Они только называются одинаково, но их значения совершенно разные.

Если мы прокрутим страницу так, чтобы элемент ушел из области видимости, то получим отрицательные значения координат по высоте. Что, в общем-то логично, так как координаты отсчитываются от верхней и левой границ клиентского окна браузера.

elementFromPoint(x, y)

Далее, рассмотрим метод документа

let elem = document.elementFromPoint(x, y);

который возвращает самый глубокий в иерархии элемент, на который указывает координата (x,y). Это бывает полезно, когда мы хотим узнать, что находится в данной позиции HTML-страницы. Для примера возьмем прежний документ и удалим ранее написанный скрипт. У нас блок div окажется поверх пунктов списка. Теперь возьмем координату (101, 110) так, чтобы мы указывали и на блок div и на список. Вызовем метод elementFromPoint с этими значениями и посмотрим, что он нам вернет:

let wnd = document.getElementById("wnd");
let elem = document.elementFromPoint(102, 110);
console.log(elem);

Получили элемент div. Но что тогда означает фраза «самый глубокий в иерархии элемент»? Разве мы не должны были бы получить элемент списка, так как он находится глубже на странице? Не совсем так: список и div являются сестринскими элементами, то есть, у них один родитель. В этом случае возвращается тот, что находится выше. А вот если мы поместим координату над списком:

let elem = document.elementFromPoint(102, 120);

то как раз и получим самый глубокий элемент, то есть, тег <li>, а не тег <ul>, который является родительским по отношению к li. Вот так следует понимать работу этого метода.

Этот метод возвращает элементы находящиеся в области видимости окна браузера, то есть, когда координаты x/y находятся в клиентской области. В других случаях, например, при отрицательных значениях координат, мы получим значение null.

Вычисление координат относительно документа

Часто в практике программирования требуется получить координаты какого-либо элемента относительно HTML-документа, а не окна браузера. Как это можно сделать? Ранее рассмотренный метод

elem.getBoundingClientRect()

возвращает координаты относительно окна (clientX, clientY). Значит, чтобы получить координаты относительно документа к ним нужно прибавить значения прокрутки документа по вертикали и горизонтали:

  • pageY = clientY + высота вертикально прокрученной части документа.
  • pageX = clientX + ширина горизонтально прокрученной части документа.

Вспоминая предыдущее занятие, величины прокрутки можно взять из свойств объекта window:

window.pageYOffset и window.pageXOffset

В результате, можно реализовать следующую функцию для вычисления координат элемента относительно документа:

let wnd = document.getElementById("wnd");
console.log( getCoordFromDocument(wnd) );
 
function getCoordFromDocument(elem) {
    let coords = elem.getBoundingClientRect();
    return {
        top: coords.top + window.pageYOffset,
        left: coords.left + window.pageXOffset
    };
}

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

Итак, мы с вами рассмотрели способы позиционирования элементов в окне браузера и научились определять и задавать их координаты положения в HTML-документе.

Видео по теме