Мастер-классы по Javascript Екатеринбург Ростов-на-Дону Москва Узнать больше...
Содержание (скрыть) Содержание (показать)

Координаты

  1. Координаты относительно окна и документа
  2. Получение координат элемента
    1. Координаты в окне: elem.getBoundingClientRect()
    2. Координаты в документе
    3. Устаревший метод: offset*
    4. Сравнение offset* с getBoundingClientRect
    5. Комбинированный подход
  3. Получение элемента по координатам: elementFromPoint(x,y)
  4. Координаты на экране screenX/screenY
  5. Итого

В браузере есть три координатные системы.

Относительно документа pageX/pageY
Точка начала координат лежит в левом верхнем углу страницы.
Относительно окна clientX/clientY
Начало координат лежит в левом верхнем углу текущей видимой области.
Относительно экрана screenX/screenY
Начало координат — левый-верхний угол экрана.

Координатами элемента, для краткости, называются координаты его левого верхнего угла.

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

Когда страница не прокручена, точки начала координат относительно окна (clientX,clientY) и документа (pageX,pageY) совпадают:

Например, координаты элемента с надписью «STANDARDS» равны расстоянию от верхней/нижней границы окна:

Прокрутим страницу, чтобы элемент был на самом верху:

Посмотрите на рисунок ниже.

  • Координата clientY изменилась. Она теперь равна 0, т.к. элемент на самом верху окна.
  • Координата pageY отсчитывается от левого-верхнего угла документа, поэтому осталась такой же. Она не зависит от прокрутки.

Итак, координаты pageX,pageY не меняются при прокрутке, в отличие от clientX,clientY. Можно сказать и по-другому. Разность pageY-clientY — это в точности размер текущей прокрученной области.

Получение координат элемента

Координаты в окне: elem.getBoundingClientRect()

Этот метод описан в стандарте W3C и поддерживается всеми современными браузерами(и даже IE6+).

Он возвращает размеры прямоугольника, который охватывает элемент, в виде объекта со свойствами: top, left, right, bottom.

Эти 4 числа — координаты верхнего левого и нижнего правого углов элемента относительно окна. Например, кликните на кнопку, чтобы увидеть координаты ее углов:

<input id="brTest" type="button" value="Show button.getBoundingClientRect()" onclick='showRect(this)'/>

<script>
function showRect(elem) {
  var r = elem.getBoundingClientRect()
  alert("Top/Left: "+r.top+" / "+r.left)
  alert("Right/Bottom: "+r.right+" / "+r.bottom)
}
</script>

Координаты elem.getBoundingClientRect() — относительно окна, а не документа.

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

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

Как устроен elem.getBoundingClientRect()?

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

В случае с блочным элементом, таким как DIV - элемент сам по себе образует прямоугольник.

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

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

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

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

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

Готовой функции для координат элемента в документе нет. Но её можно написать самим.

Наша фукнция getCoords(elem) будет брать результат elem.getBoundingClientRect() и прибавлять текущую прокрутку документа.

Результат: объект с координатами {left: .., top: ..}

function getCoords(elem) {
    // (1)
    var box = elem.getBoundingClientRect();
    
    var body = document.body;
    var docEl = document.documentElement;
    
    // (2)
    var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;
    var scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft;
    
    // (3)
    var clientTop = docEl.clientTop || body.clientTop || 0;
    var clientLeft = docEl.clientLeft || body.clientLeft || 0;
    
    // (4)
    var top  = box.top +  scrollTop - clientTop;
    var left = box.left + scrollLeft - clientLeft;
    
    // (5)
    return { top: Math.round(top), left: Math.round(left) };
}

Разберем что и зачем, по шагам:

  1. Получаем прямоугольник.
  2. Считаем прокрутку страницы. Все браузеры, кроме IE<9 поддерживают свойство pageXOffset/pageYOffset. В более старых IE, когда установлен DOCTYPE, прокрутку можно получить из documentElement, ну и наконец если DOCTYPE некорректен — использовать body.
  3. В IE документ может быть смещен относительно левого верхнего угла. Получим это смещение.
  4. Добавим прокрутку к координатам окна и вычтем смещение html/body, чтобы получить координаты всего документа.
  5. Координаты округляются вызовом Math.round() т.к. в Firefox бывают дробные пиксели.

Устаревший метод: offset*

Есть альтернативный способ нахождения координат — это пройти всю цепочку offsetParent от элемента вверх и сложить отступы offsetLeft/offsetTop.

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

Вот функция, реализующая такой подход.

function getOffsetSum(elem) {
  var top = 0, left = 0;

  while(elem) {
    top = top + parseInt(elem.offsetTop);
    left = left + parseInt(elem.offsetLeft);
    elem = elem.offsetParent;         
  }
   
  return {top: top, left: left};
}

Она работает, но у этого подхода есть как минимум два недостатка.

  1. Он ненадежный. Разные браузеры преподносят «сюрпризы», включая или выключая размер рамок и прокруток из offsetTop/Left.
  2. Он медленный, ведь нам каждый раз приходиться проходить всю цепочку offsetParent.

Конечно, можно написать кроссбраузерный код, который будет учитывать все нюансы, но давайте-ка лучше посмотрим альтернативное решение, которые работает во всех современных браузерах, включая Firefox 3+ и Opera 9.62+, Safari/Chrome и IE6+.

Сравнение offset* с getBoundingClientRect

Сравним методы для вычисления координат, описанные выше.

В следующем примере есть 3 вложенных DIV. Все они имеют border, кое-кто из них имеет position/margin/padding.

Кликните по внутреннему элементу, чтобы увидеть результаты обоих методов: getOffsetSum и getCoords, а так же реальные координаты курсора - event.pageX/pageY (мы обсудим их позже в статье Устранение IE-несовместимостей: «fixEvent»).

Все значения выводятся под DIV-ами.

Кликните, чтобы получить координаты getOffsetSum и getCoords
getOffsetSum:значение getOffsetSum()
getCoords:значение getCoords()
mouse:координаты мыши

Кликните в любом месте желтого блока и вы увидите результаты для getOffsetSum(elem) и getCoords(elem). Обратите внимание, что они обычно не совпадают.

Для того, чтобы узнать, какой же результат верный, кликните в левом верхнем углу желтого блока. Он расположен в левом верхнем углу черной границы.

Будут видны абсолютные координаты мыши, так что вы можете сравнить их с getOffsetSum/getCoords.

Попробуйте, чтобы увидеть, что getCoords всегда возвращает верное значение Smile.

Комбинированный подход

Фреймворки, которые хотят быть совместимыми со старыми браузерами, используют комбинированный подход:

function getOffset(elem) {
    if (elem.getBoundingClientRect) {
        return getCoords(elem);
    } else { // старый браузер
        return getOffsetSum(elem);
    }
}

function getOffsetSum(elem) {
  var top=0, left=0
  while(elem) {
    top = top + parseInt(elem.offsetTop)
    left = left + parseInt(elem.offsetLeft)
    elem = elem.offsetParent        
  }
   
  return {top: top, left: left}
}


function getCoords(elem) {
    var box = elem.getBoundingClientRect()
    
    var body = document.body;
    var docEl = document.documentElement;
    
    var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;
    var scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft;
    
    var clientTop = docEl.clientTop || body.clientTop || 0;
    var clientLeft = docEl.clientLeft || body.clientLeft || 0;
    
    var top  = box.top +  scrollTop - clientTop;
    var left = box.left + scrollLeft - clientLeft;
    
    return { top: Math.round(top), left: Math.round(left) };
}


function getOffset(elem) {
    if (elem.getBoundingClientRect) {
        return getCoords(elem)
    } else {
        return getOffsetSum(elem)
    }
}

Получение элемента по координатам: elementFromPoint(x,y)

Обратная задача — «получить элемент по координатам в документе» появится при работе с событиями мыши.

Для её решения есть метод document.elementFromPoint(clientX, clientY).

Параметры clientX/clientY — координаты относительно окна.

Например: введите координаты в форме ниже. Соответствующий элемент на этой странице будет подсвечен стилем background-color:red. При повторном нажатии подсветка снимается.

clientX= clientY=

Код функции подсветки:

/* x,y - координаты относительно окна */
function highlightRedXY(x,y) {
*!*
  var el = document.elementFromPoint(x, y);
*/!*
  el.style.backgroundColor = (el.style.backgroundColor == 'red') ? '' : 'red';
}

Обратите внимание — при прокрутке страницы на тех же координатах будут новые элементы. Это как раз потому, что координаты в этой функции — относительно окна, а не документа.

Координаты на экране screenX/screenY

Координаты относительно экрана screenX/screenY отсчитываются от его левого-верхнего угла. Имеется в виду именно весь экран, а не окно браузера.

Таки координаты редко используются. Но они могут быть полезны, например, при работе с мобильными устройствами.

Общая информация об экране хранится в глобальной переменной screen:

// общая ширина/высота
alert( screen.width + ' x ' + screen.height ); 

// доступная ширина/высота (за вычетом таскбара и т.п.)
alert( screen.availWidth + ' x ' + screen.availHeight); 

// есть и ряд других свойств screen (см. документацию)

Координаты левого-верхнего угла браузера на экране хранятся в window.screenX, window.screenY (не поддерживаются IE<9):

alert("Браузера находится на " + window.screenX + "," + window.screenY);

Они могут быть и меньше нуля, если окно частично вне экрана.

Координаты DOM-элемента относительно экрана получить нельзя. Браузер не предоставляет свойств и методов для этого.

Итого

У любой точки в браузере есть координаты:

  1. Относительно окна window.
  2. Относительно документа document — не меняются при прокрутке.
  3. Относительно экрана screen.

Начало координат — в левом-верхнем углу окна/документа/экрана.

Координаты элемента относительно окна: elem.getBoundingClientRect(), для получения относительно документа — добавляем прокрутку.

В старых Firefox, Chrome/Safari и Opera нескольких лет давности метода getBoundingClientRect нет, для них — можно использовать суммирование offsetTop/Left.

Координаты будут нужны нам далее, при работе с событиями мыши (координаты клика) и элементами (перемещение).


Комментарии

  1. Приветствуются комментарии, содержащие дополнения и вопросы по статье, и ответы на них.
  2. Если ваш комментарий касается задачи -- откройте её в отдельном окне и напишите там.
  3. Комментарии без смысла, с рекламой или не о статье вообще - удаляются.
Наверх

Содержание

Реклама

Нашли опечатку?

Нашли опечатку на сайте? Что-то кажется странным?
Выделите соответствующий текст и нажмите Ctrl+Enter!

Последние Комментарии

Помоги другим!

Помоги другим узнать о хорошей статье!