При разметке 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) поверх всей
страницы с положением, не зависящем от скроллинга. Но, если заменить стиль на:
то при прокрутке
элемент начинает также смещаться вместе со всей страницей. Фактически, мы здесь
имеем следующую картину (см. рисунок). В режиме 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-документе.