W3docs

Координаты в JavaScript

Координаты в JavaScript — числовые значения, описывающие позицию на странице. Узнайте о двух системах координат браузера и ключевых методах DOM.

Координаты — это числовые значения, описывающие положение на странице. Практически каждая интерактивная функция — перетаскивание, рисование, подсказки, всплывающие меню, определение попадания — в итоге сводится к считыванию координаты и размещению или сравнению чего-либо относительно неё.

Сложность в том, что браузер предоставляет две разные системы координат, и их путаница является наиболее распространённой причиной ошибок вида «мой элемент перемещается не на то место». В этой главе объясняются обе системы, способы преобразования между ними, а также ключевые методы DOM (getBoundingClientRect() и elementFromPoint()), используемые для считывания позиций элементов.

Две системы координат

Существуют две системы отсчёта, которые необходимо различать:

  • Координаты viewport (относительно окна) — отсчитываются от верхнего левого угла видимой части страницы. Они не изменяются при прокрутке. Точка в верхнем левом углу видимой области всегда равна (0, 0). Свойства: clientX / clientY.
  • Координаты документа (относительно страницы) — отсчитываются от верхнего левого угла всего документа, включая часть, прокрученную за пределы экрана. Они увеличиваются при прокрутке вниз. Свойства: pageX / pageY.

Связь между ними определяется смещением прокрутки:

pageX = clientX + window.scrollX;
pageY = clientY + window.scrollY;

Когда страница не прокручена, window.scrollX и window.scrollY равны 0, поэтому обе системы дают одинаковые значения — именно поэтому ошибки, связанные с прокруткой, не проявляются до тех пор, пока пользователь не прокрутит страницу.

Используйте координаты viewport (clientX/Y) когда…Используйте координаты документа (pageX/Y) когда…
Позиционируете элемент с position: fixedПозиционируете элемент с position: absolute
Проверяете попадание в то, что видно на экранеСохраняете место клика для последующего воспроизведения
Работаете с getBoundingClientRect()Рисуете на высоком прокручиваемом canvas

О том, как измеряются window.scrollX/Y и размер viewport, читайте в разделе Размеры окна и прокрутка в JavaScript, а о программном управлении прокруткой — в разделе Прокрутка в JavaScript.

Считывание координат из события мыши

Каждое событие мыши содержит информацию об обеих системах координат. Полная модель событий описана в разделе Основы событий мыши.

Координаты viewport: clientX / clientY

event.clientX и event.clientY возвращают положение указателя относительно viewport независимо от прокрутки. Пример ниже размещён внутри высокой страницы, чтобы вы могли прокрутить её и убедиться, что числа привязаны к видимой области:

<!-- snippet: html-result -->

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Client-Side Coordinates Example</title>
    <style>
#container {
  width: 100%;
  height: 100%;
  background-color: grey;
  min-height: 40px;
}
    </style>
</head>
<body style="height: 2000px;">
    <div id="container">Click anywhere in the grey area to see Client-Side coordinates!</div>
    <script>
        const container = document.getElementById('container');
        function showCoords(event) {
            alert("Client-Side X: " + event.clientX + ", Y: " + event.clientY);
        }
        container.addEventListener('click', showCoords);
    </script>
</body>
</html>

Координаты документа: pageX / pageY

event.pageX и event.pageY возвращают положение указателя относительно всего документа, поэтому они уже включают смещение прокрутки. Они стандартны и широко поддерживаются — вычислять их вручную не нужно. Пример ниже показывает их рядом с ручным вычислением clientX + window.scrollX, чтобы вы могли убедиться в их совпадении:

<!-- snippet: html-result -->

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Page-Side Coordinates Example</title>
    <style>
#container {
  width: 100%;
  height: 100%;
  background-color: grey;
  min-height: 40px;
}
    </style>
</head>
<body style="height: 2000px;">
    <div id="container">Click anywhere in this area to see Page-Side coordinates!</div>
    <script>
        const container = document.getElementById('container');
        function showPageCoords(event) {
            // pageX/pageY already include scroll; the manual version must match
            const manualX = event.clientX + window.scrollX;
            const manualY = event.clientY + window.scrollY;
            alert("pageX/Y: " + event.pageX + ", " + event.pageY +
                  "\nclientX+scrollX: " + manualX + ", " + manualY);
        }
        container.addEventListener('click', showPageCoords);
    </script>
</body>
</html>

Экранные координаты: screenX / screenY

event.screenX и event.screenY измеряют положение указателя относительно всего физического экрана, а не только окна браузера. Они полезны для отслеживания курсора на нескольких мониторах или позиционирования всплывающего окна window.open(), но для работы внутри страницы почти всегда предпочтительнее использовать clientX/Y или pageX/Y.

Считывание позиции элемента: getBoundingClientRect()

Чтобы найти положение элемента (а не курсора мыши), вызовите getBoundingClientRect(). Метод возвращает объект DOMRect, свойства которого top, right, bottom, left, width и height указаны в координатах viewport — той же системе, что и clientX/Y.

const rect = element.getBoundingClientRect();
// rect.left, rect.top   → top-left corner, relative to the viewport
// rect.width, rect.height → rendered size including borders/padding

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

function getDocumentCoords(el) {
  const rect = el.getBoundingClientRect();
  return {
    top: rect.top + window.scrollY,
    left: rect.left + window.scrollX,
  };
}

Эта комбинация — считывание через getBoundingClientRect() и добавление scrollX/Y при необходимости получить координаты страницы — понадобится вам постоянно.

Поиск элемента по координатам: elementFromPoint()

На обратный вопрос — «что находится под этими координатами?» — отвечает метод document.elementFromPoint(x, y). Он принимает координаты viewport (соответствующие clientX/Y) и возвращает самый верхний элемент в указанной точке или null, если точка находится за пределами viewport.

document.addEventListener('click', (event) => {
  const el = document.elementFromPoint(event.clientX, event.clientY);
  console.log('You clicked on:', el.tagName);
});

Распространённая ошибка: передать в метод pageX/pageY на прокрученной странице — тогда он вернёт неправильный элемент (или null), потому что ожидает координаты viewport, а не документа.

Управление позицией элементов с помощью координат

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

Пример: перетаскиваемый HTML-элемент

<!-- snippet: html-result -->

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Draggable Element Example</title>
    <style>
        #draggable {
            width: 100px;
            height: 100px;
            background-color: red;
            position: absolute;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <div id="draggable"></div>
    <script>
        const elem = document.getElementById('draggable');
        let shiftX, shiftY;

        function onMouseDown(event) {
            shiftX = event.clientX - elem.getBoundingClientRect().left;
            shiftY = event.clientY - elem.getBoundingClientRect().top;

            function moveAt(pageX, pageY) {
                elem.style.left = pageX - shiftX + 'px';
                elem.style.top = pageY - shiftY + 'px';
            }

            function onMouseMove(event) {
                moveAt(event.clientX + window.scrollX, event.clientY + window.scrollY);
            }

            document.addEventListener('mousemove', onMouseMove);
            document.addEventListener('mouseup', function stopDrag() {
                document.removeEventListener('mousemove', onMouseMove);
                document.removeEventListener('mouseup', stopDrag);
                elem.removeEventListener('mousedown', onMouseDown);
            });
        }

        elem.addEventListener('mousedown', onMouseDown);
        elem.addEventListener('dragstart', function() { return false; });
    </script>
</body>
</html>
Примечание

Для лучшей производительности при анимациях рассмотрите использование transform: translate() вместо left / top, так как это позволяет избежать пересчёта макета. Для совместимости с мобильными устройствами добавьте обработчики событий touchstart, touchmove и touchend наряду с событиями мыши.

Анимация с использованием координат

Вы также можете управлять движением, обновляя координаты элемента в каждом кадре с помощью requestAnimationFrame. Умножение на временну́ю дельту обеспечивает постоянную скорость независимо от частоты обновления экрана:

Пример: анимированный движущийся объект

<!-- snippet: html-result -->

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Animation Using Coordinates</title>
</head>
<body>
    <div id="animateMe" style="width: 50px; height: 50px; background: blue; position: absolute;"></div>
    <button id="stopBtn">Stop Animation</button>
    <script>
        const target = document.getElementById('animateMe');
        const stopBtn = document.getElementById('stopBtn');
        let pos = 0;
        let isRunning = true;
        let lastTime = performance.now();

        function animate(currentTime) {
            if (!isRunning) return;
            const delta = (currentTime - lastTime) / 16; // Normalize to ~60fps
            lastTime = currentTime;
            if (pos >= 350) {
                pos = 0;
            }
            pos += delta;
            target.style.left = pos + 'px';
            requestAnimationFrame(animate);
        }

        stopBtn.addEventListener('click', () => { isRunning = false; });
        requestAnimationFrame(animate);
    </script>
</body>
</html>
Информация

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

Заключение

Главное, что нужно запомнить: браузер использует две системы координат — viewport (clientX/Y, getBoundingClientRect(), elementFromPoint()) и документа (pageX/Y), и преобразование между ними выполняется путём прибавления или вычитания window.scrollX/Y. Разберитесь с этим — и большинство ошибок позиционирования исчезнут.

Далее изучите разделы Размеры окна и прокрутка, Прокрутка — для управления viewport, и Основы событий мыши — для событий, которые передают эти координаты.

Практика

Практика
Что представляют собой клиентские координаты в JavaScript?
Что представляют собой клиентские координаты в JavaScript?
Was this page helpful?