Координаты в окне

Для того, чтобы поместить один элемент рядом с другим на странице, а также двигать его произвольным образом, к примеру, рядом с указателем мыши – используются координаты.

Координатная система относительно окна браузера начинается в левом-верхнем углу текущей видимой области окна.

Мы будем называть координаты в ней clientX/clientY.

getBoundingClientRect()

Метод elem.getBoundingClientRect() возвращает координаты элемента, под которыми понимаются размеры «воображаемого прямоугольника», который охватывает весь элемент.

Координаты возвращаются в виде объекта со свойствами:

  • top – Y-координата верхней границы элемента,
  • left – X-координата левой границы,
  • right – X-координата правой границы,
  • bottom – Y-координата нижней границы.

Например:

Координаты относительно окна не учитывают прокрутку, они высчитываются от границ текущей видимой области.

Иначе говоря, если страницу прокрутить, то элемент поднимется выше или опустится ниже – его координаты относительно окна изменятся.

Например, кликните на кнопку, чтобы увидеть её координаты:

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

  • Координаты могут быть дробными – это нормально, так как они возвращаются из внутренних структур браузера.
  • Координаты могут быть и отрицательными, например если прокрутить страницу так, что верх элемента будет выходить за верхнуюю границу окна, то его top-координата будет меньше нуля.
  • Некоторые современные браузеры также добавляют к результату getBoundingClientRect свойства для ширины и высоты: width/height, но их можно получить и простым вычитанием: height = bottom - top, width = right - left.
Координаты right/bottom отличаются от CSS-свойств

Если рассмотреть позиционирование элементов при помощи CSS-свойства position, то там тоже указываются left, right, top, bottom.

Однако, по CSS свойство right задаёт расстояние от правой границы, а bottom – от нижней.

Если вы взглянете на иллюстрацию выше, то увидите, что в JavaScript это не так. Все координаты отсчитываются слева/сверху, в том числе и эти.

Метод elem.getBoundingClientRect() изнутри

Браузер отображает любое содержимое, используя прямоугольники.

В случае с блочным элементом, таким как DIV, элемент сам по себе образует прямоугольник. Но если элемент строчный и содержит в себе длинный текст, то каждая строка будет отдельным прямоугольником, с одинаковой высотой но разной длиной (у каждой строки – своя длина).

Более подробно это описано в: спецификации.

Если обобщить, содержимое элемента может отображаться в одном прямоугольнике или в нескольких.

Все эти прямоугольники можно получить с помощью elem.getClientRects(). А метод elem.getBoundingClientRect() возвращает один охватывающий прямоугольник для всех getClientRects().

elementFromPoint(x, y)

Возвращает элемент, который находится на координатах (x, y) относительно окна.

Синтаксис:

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

Например, код ниже выделяет и выводит тег у элемента, который сейчас в середине окна:

var centerX = document.documentElement.clientWidth / 2;
var centerY = document.documentElement.clientHeight / 2;

var elem = document.elementFromPoint(centerX, centerY);

elem.style.background = "red";
alert( elem.tagName );
elem.style.background = "";

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

Для координат вне окна elementFromPoint возвращает null

Метод document.elementFromPoint(x,y) работает только если координаты (x,y) находятся в пределах окна.

Если одна из них отрицательна или больше чем ширина/высота окна – он возвращает null.

В большинстве случаев использования это не является проблемой, но нужно обязательно иметь такую возможность в виду.

Типичная ошибка, которая может возникнуть, если не проверять наличие elem:

var elem = document.elementFromPoint(centerX, centerY);
// если координаты вне окна, то elem = null
elem.style.background = ''; // ошибка!

Координаты для position:fixed

Координаты обычно требуются не просто так, а, например, чтобы переместить элемент на них.

В CSS для позиционирования элемента относительно окна используется свойство position:fixed. Как правило, вместе с ним идут и координаты, например left/top.

Например, функция createMessageUnder из кода ниже покажет сообщение под элементом elem:

var elem = document.getElementById("coords-show-mark");

function createMessageUnder(elem, text) {
  // получить координаты
  var coords = elem.getBoundingClientRect();

  // создать элемент для сообщения
  var message = document.createElement('div');
  // стиль лучше задавать классом
  message.style.cssText = "position:fixed; color: red";

  // к координатам обязательно добавляем "px"!
  message.style.left = coords.left + "px";
  message.style.top = coords.bottom + "px";

  message.innerHTML = text;

  return message;
}

// Использование
// добавить на 5 сек в документ
var message = createMessageUnder(elem, 'Привет, мир!');
document.body.appendChild(message);
setTimeout(function() {
  document.body.removeChild(message);
}, 5000);

Нажмите на кнопку, чтобы запустить его:

Этот код можно модифицировать, чтобы показывать сообщение слева, справа, сверху, делать это вместе с CSS-анимацией и так далее. Для этого нужно всего лишь понимать, как получить координаты.

Заметим, однако, важную деталь: при прокрутке страницы сообщение будет визуально отдаляться от кнопки.

Причина очевидна, ведь оно использует position: fixed, так что при прокрутке остаётся на месте, а страница скроллируется.

Как сделать, чтобы сообщение было именно на конкретном месте документа, а не окна, мы рассмотрим в следующей главе.

Задачи

важность: 5

Координаты внешних углов

Координаты элемента возвращаются функцией elem.getBoundingClientRect. Она возвращает все координаты относительно окна в виде объекта со свойствами left, top, right, bottom. Некоторые браузеры также добавляют width, height.

Так что координаты верхнего-левого coords1 и правого-нижнего coords4 внешних углов:

var coords = elem.getBoundingClientRect();

var coords1 = [coords.left, coords.top];
var coords2 = [coords.right, coords.bottom];

Левый-верхний угол внутри

Этот угол отстоит от наружных границ на размер рамки, который доступен через clientLeft/clientTop:

var coords3 = [coords.left + field.clientLeft, coords.top + field.clientTop];

Правый-нижний угол внутри

Этот угол отстоит от правой-нижней наружной границы на размер рамки. Так как нужная рамка находится справа-внизу, то специальных свойств для нее нет, но мы можем получить этот размер из CSS:

var coords4 = [
  coords.right - parseInt(getComputedStyle(field).borderRightWidth),
  coords.bottom - parseInt(getComputedStyle(field).borderBottomWidth)
]

Можно получить их альтернативным путем, прибавив clientWidth/clientHeight к координатам левого-верхнего внутреннего угла. Получится то же самое, пожалуй даже быстрее и изящнее.

var coords4 = [
  coords.left + elem.clientLeft + elem.clientWidth,
  coords.top + elem.clientTop + elem.clientHeight
]

Открыть решение в песочнице.

В ифрейме ниже вы видите документ с зеленым «полем».

При помощи JavaScript найдите координаты указанных стрелками углов относительно окна браузера.

Для тестирования в документ добавлено удобство: клик в любом месте отображает координаты мыши относительно окна.

Ваш код должен при помощи DOM получить четыре пары координат:

  1. Левый-верхний угол снаружи, это просто.
  2. Правый-нижний угол снаружи, это тоже просто.
  3. Левый-верхний угол внутри, это чуть сложнее.
  4. Правый-нижний угол внутри, это ещё сложнее, но можно сделать даже несколькими способами.

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

P.S. Код не должен быть как-то привязан к конкретным размерам элемента, стилям, наличию или отсутствию рамки.

Открыть песочницу для задачи.

важность: 5

Создайте функцию positionAt(anchor, position, elem), которая позиционирует элемент elem, в зависимости от position, сверху ("top"), справа ("right") или снизу ("bottom") от элемента anchor.

Используйте её, чтобы сделать функцию showNote(anchor, position, html), которая показывает элемент с классом note и текстом html на позиции position рядом с элементом anchor.

Выведите заметки как здесь:

Открыть песочницу для задачи.

Карта учебника

Комментарии

перед тем как писать…
  • Приветствуются комментарии, содержащие дополнения и вопросы по статье, и ответы на них.
  • Для одной строки кода используйте тег <code>, для нескольких строк кода — тег <pre>, если больше 10 строк — ссылку на песочницу (plnkr, JSBin, codepen…)
  • Если что-то непонятно в статье — пишите, что именно и с какого места.